fuel_vm/interpreter/
gas.rs

1use super::Interpreter;
2use crate::{
3    constraints::reg_key::*,
4    error::SimpleResult,
5    prelude::{
6        Bug,
7        BugVariant,
8    },
9    profiler::Profiler,
10};
11
12use fuel_asm::{
13    PanicReason,
14    RegId,
15};
16use fuel_tx::DependentCost;
17use fuel_types::{
18    ContractId,
19    Word,
20};
21
22#[cfg(test)]
23mod tests;
24
25impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal> {
26    /// Global remaining gas amount
27    pub fn remaining_gas(&self) -> Word {
28        self.registers[RegId::GGAS]
29    }
30
31    /// Sets the amount of gas available for execution to both CGAS and GGAS.
32    /// Only useful in contexts where CGAS and GGAS are the same,
33    /// i.e. predicates and testing.
34    pub(crate) fn set_gas(&mut self, gas: Word) {
35        self.registers[RegId::GGAS] = gas;
36        self.registers[RegId::CGAS] = gas;
37    }
38
39    pub(crate) fn dependent_gas_charge(
40        &mut self,
41        gas_cost: DependentCost,
42        arg: Word,
43    ) -> SimpleResult<()> {
44        let current_contract = self.contract_id();
45        let SystemRegisters {
46            pc, ggas, cgas, is, ..
47        } = split_registers(&mut self.registers).0;
48        let profiler = ProfileGas {
49            pc: pc.as_ref(),
50            is: is.as_ref(),
51            current_contract,
52            profiler: &mut self.profiler,
53        };
54        dependent_gas_charge(cgas, ggas, profiler, gas_cost, arg)
55    }
56
57    pub(crate) fn dependent_gas_charge_without_base(
58        &mut self,
59        gas_cost: DependentCost,
60        arg: Word,
61    ) -> SimpleResult<()> {
62        let current_contract = self.contract_id();
63        let SystemRegisters {
64            pc, ggas, cgas, is, ..
65        } = split_registers(&mut self.registers).0;
66        let profiler = ProfileGas {
67            pc: pc.as_ref(),
68            is: is.as_ref(),
69            current_contract,
70            profiler: &mut self.profiler,
71        };
72        dependent_gas_charge_without_base(cgas, ggas, profiler, gas_cost, arg)
73    }
74
75    /// Do a gas charge with the given amount, panicing when running out of gas.
76    pub fn gas_charge(&mut self, gas: Word) -> SimpleResult<()> {
77        let current_contract = self.contract_id();
78        let SystemRegisters {
79            pc, ggas, cgas, is, ..
80        } = split_registers(&mut self.registers).0;
81
82        let profiler = ProfileGas {
83            pc: pc.as_ref(),
84            is: is.as_ref(),
85            current_contract,
86            profiler: &mut self.profiler,
87        };
88        gas_charge(cgas, ggas, profiler, gas)
89    }
90}
91
92pub(crate) fn dependent_gas_charge_without_base(
93    mut cgas: RegMut<CGAS>,
94    ggas: RegMut<GGAS>,
95    mut profiler: ProfileGas<'_>,
96    gas_cost: DependentCost,
97    arg: Word,
98) -> SimpleResult<()> {
99    let cost = gas_cost.resolve_without_base(arg);
100    profiler.profile(cgas.as_ref(), cost);
101    gas_charge_inner(cgas.as_mut(), ggas, cost)
102}
103
104pub(crate) fn dependent_gas_charge(
105    mut cgas: RegMut<CGAS>,
106    ggas: RegMut<GGAS>,
107    mut profiler: ProfileGas<'_>,
108    gas_cost: DependentCost,
109    arg: Word,
110) -> SimpleResult<()> {
111    let cost = gas_cost.resolve(arg);
112    profiler.profile(cgas.as_ref(), cost);
113    gas_charge_inner(cgas.as_mut(), ggas, cost)
114}
115
116pub(crate) fn gas_charge(
117    cgas: RegMut<CGAS>,
118    ggas: RegMut<GGAS>,
119    mut profiler: ProfileGas<'_>,
120    gas: Word,
121) -> SimpleResult<()> {
122    profiler.profile(cgas.as_ref(), gas);
123    gas_charge_inner(cgas, ggas, gas)
124}
125
126fn gas_charge_inner(
127    mut cgas: RegMut<CGAS>,
128    mut ggas: RegMut<GGAS>,
129    gas: Word,
130) -> SimpleResult<()> {
131    if *cgas > *ggas {
132        Err(Bug::new(BugVariant::GlobalGasLessThanContext).into())
133    } else if gas > *cgas {
134        *ggas = (*ggas)
135            .checked_sub(*cgas)
136            .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
137        *cgas = 0;
138
139        Err(PanicReason::OutOfGas.into())
140    } else {
141        *cgas = (*cgas)
142            .checked_sub(gas)
143            .ok_or_else(|| Bug::new(BugVariant::ContextGasUnderflow))?;
144        *ggas = (*ggas)
145            .checked_sub(gas)
146            .ok_or_else(|| Bug::new(BugVariant::GlobalGasUnderflow))?;
147
148        Ok(())
149    }
150}
151
152#[allow(dead_code)]
153pub(crate) struct ProfileGas<'a> {
154    pub pc: Reg<'a, PC>,
155    pub is: Reg<'a, IS>,
156    pub current_contract: Option<ContractId>,
157    pub profiler: &'a mut Profiler,
158}
159
160impl<'a> ProfileGas<'a> {
161    #[allow(unused_variables)]
162    pub(crate) fn profile(&mut self, cgas: Reg<CGAS>, gas: Word) {
163        #[cfg(feature = "profile-coverage")]
164        {
165            let location =
166                super::current_location(self.current_contract, self.pc, self.is);
167            self.profiler.set_coverage(location);
168        }
169
170        #[cfg(feature = "profile-gas")]
171        {
172            let gas_use = gas.min(*cgas);
173            let location =
174                super::current_location(self.current_contract, self.pc, self.is);
175            self.profiler.add_gas(location, gas_use);
176        }
177    }
178}