1pub use checked::*;
6use serde::{Deserialize, Serialize};
7
8use crate::{base_types::ObjectID, gas_model::gas_v2::PerObjectStorage};
9
10#[sui_macros::with_checked_arithmetic]
11pub mod checked {
12
13 use crate::gas::GasUsageReport;
14 use crate::gas_model::gas_predicates::check_for_gas_price_too_high;
15 use crate::{
16 ObjectID,
17 effects::{TransactionEffects, TransactionEffectsAPI},
18 error::{ExecutionError, SuiResult, UserInputError, UserInputResult},
19 gas_model::{gas_v2::SuiGasStatus as SuiGasStatusV2, tables::GasStatus},
20 object::Object,
21 sui_serde::{BigInt, Readable},
22 transaction::ObjectReadResult,
23 };
24 use enum_dispatch::enum_dispatch;
25 use itertools::MultiUnzip;
26 use schemars::JsonSchema;
27 use serde::{Deserialize, Serialize};
28 use serde_with::serde_as;
29 use sui_protocol_config::ProtocolConfig;
30
31 #[enum_dispatch]
32 pub trait SuiGasStatusAPI {
33 fn is_unmetered(&self) -> bool;
34 fn move_gas_status(&self) -> &GasStatus;
35 fn move_gas_status_mut(&mut self) -> &mut GasStatus;
36 fn bucketize_computation(&mut self, aborted: Option<bool>) -> Result<(), ExecutionError>;
37 fn summary(&self) -> GasCostSummary;
38 fn gas_budget(&self) -> u64;
39 fn gas_price(&self) -> u64;
40 fn reference_gas_price(&self) -> u64;
41 fn storage_gas_units(&self) -> u64;
42 fn storage_rebate(&self) -> u64;
43 fn unmetered_storage_rebate(&self) -> u64;
44 fn gas_used(&self) -> u64;
45 fn reset_storage_cost_and_rebate(&mut self);
46 fn charge_storage_read(&mut self, size: usize) -> Result<(), ExecutionError>;
47 fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError>;
48 fn track_storage_mutation(
49 &mut self,
50 object_id: ObjectID,
51 new_size: usize,
52 storage_rebate: u64,
53 ) -> u64;
54 fn charge_storage_and_rebate(&mut self) -> Result<(), ExecutionError>;
55 fn adjust_computation_on_out_of_gas(&mut self);
56 fn gas_usage_report(&self) -> GasUsageReport;
57 }
58
59 #[enum_dispatch(SuiGasStatusAPI)]
61 #[derive(Debug)]
62 pub enum SuiGasStatus {
63 V2(SuiGasStatusV2),
66 }
67
68 impl SuiGasStatus {
69 pub fn new(
70 gas_budget: u64,
71 gas_price: u64,
72 reference_gas_price: u64,
73 config: &ProtocolConfig,
74 ) -> SuiResult<Self> {
75 if gas_price < reference_gas_price {
80 return Err(UserInputError::GasPriceUnderRGP {
81 gas_price,
82 reference_gas_price,
83 }
84 .into());
85 }
86 if check_for_gas_price_too_high(config.gas_model_version())
87 && gas_price >= config.max_gas_price()
88 {
89 return Err(UserInputError::GasPriceTooHigh {
90 max_gas_price: config.max_gas_price(),
91 }
92 .into());
93 }
94
95 Ok(Self::V2(SuiGasStatusV2::new_with_budget(
96 gas_budget,
97 gas_price,
98 reference_gas_price,
99 config,
100 )))
101 }
102
103 pub fn new_unmetered() -> Self {
104 Self::V2(SuiGasStatusV2::new_unmetered())
105 }
106
107 pub fn check_gas_balance(
108 &self,
109 gas_objs: &[&ObjectReadResult],
110 gas_budget: u64,
111 ) -> UserInputResult {
112 match self {
113 Self::V2(status) => status.check_gas_balance(gas_objs, gas_budget),
114 }
115 }
116
117 pub fn check_gas_objects(&self, gas_objs: &[&ObjectReadResult]) -> UserInputResult {
118 match self {
119 Self::V2(status) => status.check_gas_objects(gas_objs),
120 }
121 }
122
123 pub fn check_gas_data(
124 &self,
125 gas_objs: &[&ObjectReadResult],
126 gas_budget: u64,
127 ) -> UserInputResult {
128 match self {
129 Self::V2(status) => status.check_gas_data(gas_objs, gas_budget),
130 }
131 }
132
133 pub fn gas_price(&self) -> u64 {
134 match self {
135 Self::V2(status) => status.gas_price(),
136 }
137 }
138 }
139
140 #[serde_as]
165 #[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
166 #[serde(rename_all = "camelCase")]
167 pub struct GasCostSummary {
168 #[schemars(with = "BigInt<u64>")]
170 #[serde_as(as = "Readable<BigInt<u64>, _>")]
171 pub computation_cost: u64,
172 #[schemars(with = "BigInt<u64>")]
174 #[serde_as(as = "Readable<BigInt<u64>, _>")]
175 pub storage_cost: u64,
176 #[schemars(with = "BigInt<u64>")]
179 #[serde_as(as = "Readable<BigInt<u64>, _>")]
180 pub storage_rebate: u64,
181 #[schemars(with = "BigInt<u64>")]
183 #[serde_as(as = "Readable<BigInt<u64>, _>")]
184 pub non_refundable_storage_fee: u64,
185 }
186
187 impl GasCostSummary {
188 pub fn new(
189 computation_cost: u64,
190 storage_cost: u64,
191 storage_rebate: u64,
192 non_refundable_storage_fee: u64,
193 ) -> GasCostSummary {
194 GasCostSummary {
195 computation_cost,
196 storage_cost,
197 storage_rebate,
198 non_refundable_storage_fee,
199 }
200 }
201
202 pub fn gas_used(&self) -> u64 {
203 self.computation_cost + self.storage_cost
204 }
205
206 pub fn sender_rebate(&self, storage_rebate_rate: u64) -> u64 {
209 const BASIS_POINTS: u128 = 10000;
212 (((self.storage_rebate as u128 * storage_rebate_rate as u128)
213 + (BASIS_POINTS / 2)) / BASIS_POINTS) as u64
215 }
216
217 pub fn net_gas_usage(&self) -> i64 {
219 self.gas_used() as i64 - self.storage_rebate as i64
220 }
221
222 pub fn new_from_txn_effects<'a>(
223 transactions: impl Iterator<Item = &'a TransactionEffects>,
224 ) -> GasCostSummary {
225 let (storage_costs, computation_costs, storage_rebates, non_refundable_storage_fee): (
226 Vec<u64>,
227 Vec<u64>,
228 Vec<u64>,
229 Vec<u64>,
230 ) = transactions
231 .map(|e| {
232 (
233 e.gas_cost_summary().storage_cost,
234 e.gas_cost_summary().computation_cost,
235 e.gas_cost_summary().storage_rebate,
236 e.gas_cost_summary().non_refundable_storage_fee,
237 )
238 })
239 .multiunzip();
240
241 GasCostSummary {
242 storage_cost: storage_costs.iter().sum(),
243 computation_cost: computation_costs.iter().sum(),
244 storage_rebate: storage_rebates.iter().sum(),
245 non_refundable_storage_fee: non_refundable_storage_fee.iter().sum(),
246 }
247 }
248 }
249
250 impl std::fmt::Display for GasCostSummary {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 write!(
253 f,
254 "computation_cost: {}, storage_cost: {}, storage_rebate: {}, non_refundable_storage_fee: {}",
255 self.computation_cost,
256 self.storage_cost,
257 self.storage_rebate,
258 self.non_refundable_storage_fee,
259 )
260 }
261 }
262
263 impl std::ops::AddAssign<&Self> for GasCostSummary {
264 fn add_assign(&mut self, other: &Self) {
265 self.computation_cost += other.computation_cost;
266 self.storage_cost += other.storage_cost;
267 self.storage_rebate += other.storage_rebate;
268 self.non_refundable_storage_fee += other.non_refundable_storage_fee;
269 }
270 }
271
272 impl std::ops::AddAssign<Self> for GasCostSummary {
273 fn add_assign(&mut self, other: Self) {
274 self.add_assign(&other)
275 }
276 }
277
278 pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) {
283 let gas_coin = gas_object.data.try_as_move_mut().unwrap();
285 let balance = gas_coin.get_coin_value_unsafe();
286 let new_balance = if charge_or_rebate < 0 {
287 balance + (-charge_or_rebate as u64)
288 } else {
289 assert!(balance >= charge_or_rebate as u64);
290 balance - charge_or_rebate as u64
291 };
292 gas_coin.set_coin_value_unsafe(new_balance)
293 }
294
295 pub fn get_gas_balance(gas_object: &Object) -> UserInputResult<u64> {
296 if let Some(move_obj) = gas_object.data.try_as_move() {
297 if !move_obj.type_().is_gas_coin() {
298 return Err(UserInputError::InvalidGasObject {
299 object_id: gas_object.id(),
300 });
301 }
302 Ok(move_obj.get_coin_value_unsafe())
303 } else {
304 Err(UserInputError::InvalidGasObject {
305 object_id: gas_object.id(),
306 })
307 }
308 }
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct GasUsageReport {
313 pub cost_summary: GasCostSummary,
314 pub gas_used: u64,
315 pub gas_budget: u64,
316 pub gas_price: u64,
317 pub reference_gas_price: u64,
318 pub storage_gas_price: u64,
319 pub rebate_rate: u64,
320 pub per_object_storage: Vec<(ObjectID, PerObjectStorage)>,
321}