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