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