sui_adapter_latest/static_programmable_transactions/metering/
translation_meter.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::gas_charger::GasCharger;
5use crate::static_programmable_transactions::loading::ast::{DeserializedPackage, PackagePayload};
6use sui_protocol_config::ProtocolConfig;
7use sui_types::error::ExecutionErrorTrait;
8use sui_types::execution_status::ExecutionErrorKind;
9
10/// The [`TranslationMeter`] is responsible for metering gas usage for various operations
11/// during the translation of a transaction. It interacts with and exposes interfaces to the
12/// [`GasCharger`] it holds in order to deduct gas based on the operations performed.
13///
14/// It holds a reference to the `ProtocolConfig` to access protocol-specific configuration
15/// parameters that may influence gas costs and limits.
16pub struct TranslationMeter<'pc, 'gas> {
17    protocol_config: &'pc ProtocolConfig,
18    charger: &'gas mut GasCharger,
19}
20
21impl<'pc, 'gas> TranslationMeter<'pc, 'gas> {
22    pub fn new(
23        protocol_config: &'pc ProtocolConfig,
24        gas_charger: &'gas mut GasCharger,
25    ) -> TranslationMeter<'pc, 'gas> {
26        TranslationMeter {
27            protocol_config,
28            charger: gas_charger,
29        }
30    }
31
32    pub fn charge_base_inputs<E: ExecutionErrorTrait>(
33        &mut self,
34        num_inputs: usize,
35    ) -> Result<(), E> {
36        let amount = (num_inputs as u64)
37            .max(1)
38            .saturating_mul(self.protocol_config.translation_per_input_base_charge());
39        self.charge(amount)
40    }
41
42    pub fn charge_pure_input_bytes<E: ExecutionErrorTrait>(
43        &mut self,
44        num_bytes: usize,
45    ) -> Result<(), E> {
46        let amount = (num_bytes as u64).max(1).saturating_mul(
47            self.protocol_config
48                .translation_pure_input_per_byte_charge(),
49        );
50        self.charge(amount)
51    }
52
53    pub fn charge_base_command<E: ExecutionErrorTrait>(
54        &mut self,
55        num_args: usize,
56    ) -> Result<(), E> {
57        let amount = (num_args as u64)
58            .max(1)
59            .saturating_mul(self.protocol_config.translation_per_command_base_charge());
60        self.charge(amount)
61    }
62
63    /// Charge gas for loading types based on the number of type nodes loaded.
64    /// The cost is calculated as `num_type_nodes * TYPE_LOAD_PER_NODE_MULTIPLIER`.
65    /// This function assumes that `num_type_nodes` is non-zero.
66    pub fn charge_num_type_nodes<E: ExecutionErrorTrait>(
67        &mut self,
68        num_type_nodes: u64,
69    ) -> Result<(), E> {
70        let amount = num_type_nodes
71            .max(1)
72            .saturating_mul(self.protocol_config.translation_per_type_node_charge());
73        self.charge(amount)
74    }
75
76    pub fn charge_num_type_references<E: ExecutionErrorTrait>(
77        &mut self,
78        num_type_references: u64,
79    ) -> Result<(), E> {
80        let amount = self.reference_cost_formula(num_type_references.max(1))?;
81        let amount =
82            amount.saturating_mul(self.protocol_config.translation_per_reference_node_charge());
83        self.charge(amount)
84    }
85
86    pub fn charge_num_linkage_entries<E: ExecutionErrorTrait>(
87        &mut self,
88        num_linkage_entries: usize,
89    ) -> Result<(), E> {
90        let amount = (num_linkage_entries as u64)
91            .saturating_mul(self.protocol_config.translation_per_linkage_entry_charge())
92            .max(1);
93        self.charge(amount)
94    }
95
96    pub fn charge_package_load<E: ExecutionErrorTrait>(
97        &mut self,
98        payload: &PackagePayload,
99    ) -> Result<(), E> {
100        match payload {
101            PackagePayload::Serialized(_) => {
102                // Payload stays serialized; deserialization (and charge) happen at execution time.
103                Ok(())
104            }
105            PackagePayload::Deserialized(DeserializedPackage { total_bytes, .. }) => {
106                self.charger.charge_publish_package(*total_bytes)?;
107                Ok(())
108            }
109        }
110    }
111
112    // We use a non-linear cost function for type references to account for the increased
113    // complexity they introduce. The cost is calculated as:
114    // cost = (num_type_references * (num_type_references + 1)) / 2
115    //
116    // Take &self to access protocol config if needed in the future.
117    fn reference_cost_formula<E: ExecutionErrorTrait>(&self, n: u64) -> Result<u64, E> {
118        let Some(n_succ) = n.checked_add(1) else {
119            invariant_violation!("u64 overflow when calculating type reference cost")
120        };
121        Ok(n.saturating_mul(n_succ) / 2)
122    }
123
124    // Charge gas using a point charge mechanism based on the cumulative number of units charged so
125    // far.
126    fn charge<E: ExecutionErrorTrait>(&mut self, amount: u64) -> Result<(), E> {
127        debug_assert!(amount > 0);
128        self.charger
129            .move_gas_status_mut()
130            .deduct_gas(amount.into())
131            .map_err(Self::gas_error)
132    }
133
134    fn gas_error<T, E>(e: T) -> E
135    where
136        T: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
137        E: ExecutionErrorTrait,
138    {
139        E::new_with_source(ExecutionErrorKind::InsufficientGas, e)
140    }
141}