cfx_execute_helper/observer/
gasman.rs

1use cfx_executor::{
2    observer::{
3        CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer,
4        OpcodeTracer, SetAuthTracer, StorageTracer,
5    },
6    stack::FrameResult,
7};
8use cfx_parameters::{
9    block::CROSS_SPACE_GAS_RATIO,
10    internal_contract_addresses::CROSS_SPACE_CONTRACT_ADDRESS,
11};
12use cfx_types::U256;
13use cfx_vm_types::ActionParams;
14
15use typemap::ShareDebugMap;
16
17const EVM_RATIO: (u64, u64) = (64, 63);
18const CROSS_SPACE_RATIO: (u64, u64) = (CROSS_SPACE_GAS_RATIO, 1);
19
20struct FrameGasInfo {
21    init_gas: U256,
22    gas_cost_in_subcall: U256,
23    gas_limit_for_subcall: U256,
24    cross_space_internal: bool,
25}
26
27impl FrameGasInfo {
28    #[inline]
29    fn gas_cost(&self, gas_left: &U256) -> U256 {
30        // Due to gas stipend, the gas_left could be larger than gas cost.
31        self.init_gas.saturating_sub(*gas_left)
32    }
33
34    #[inline]
35    fn gas_cost_this_level(&self, gas_left: &U256) -> U256 {
36        self.gas_cost(gas_left)
37            .saturating_sub(self.gas_cost_in_subcall)
38    }
39
40    #[inline]
41    fn minimum_init_gas(&self, gas_left: &U256, ratio: (u64, u64)) -> U256 {
42        let (numerator, denominator) = ratio;
43        self.gas_cost_this_level(gas_left)
44            + (self.gas_limit_for_subcall * numerator + denominator - 1)
45                / denominator
46    }
47}
48
49#[derive(Default)]
50pub struct GasMan {
51    gas_limit: U256,
52    gas_record: Vec<FrameGasInfo>,
53}
54
55impl DrainTrace for GasMan {
56    fn drain_trace(self, map: &mut ShareDebugMap) {
57        map.insert::<GasLimitEstimation>(self.gas_required());
58    }
59}
60
61pub struct GasLimitEstimation;
62
63impl typemap::Key for GasLimitEstimation {
64    type Value = U256;
65}
66
67impl GasMan {
68    pub fn gas_required(&self) -> U256 { self.gas_limit }
69
70    fn record_call_create(
71        &mut self, gas_pass_in: &U256, cross_space_internal: bool,
72    ) {
73        self.gas_record.push(FrameGasInfo {
74            init_gas: gas_pass_in.clone(),
75            gas_cost_in_subcall: U256::zero(),
76            gas_limit_for_subcall: U256::zero(),
77            cross_space_internal,
78        })
79    }
80
81    fn record_return(&mut self, gas_left: &U256) {
82        let child_level = self.gas_record.pop().unwrap();
83        let ratio = if child_level.cross_space_internal {
84            CROSS_SPACE_RATIO
85        } else {
86            EVM_RATIO
87        };
88
89        if let Some(FrameGasInfo {
90            gas_cost_in_subcall,
91            gas_limit_for_subcall,
92            ..
93        }) = self.gas_record.last_mut()
94        {
95            *gas_cost_in_subcall += child_level.gas_cost(gas_left);
96            *gas_limit_for_subcall +=
97                child_level.minimum_init_gas(gas_left, ratio);
98        } else {
99            self.gas_limit = child_level.minimum_init_gas(gas_left, ratio);
100        }
101    }
102}
103
104impl CallTracer for GasMan {
105    fn record_call(&mut self, params: &ActionParams) {
106        let cross_space_internal =
107            params.code_address == CROSS_SPACE_CONTRACT_ADDRESS;
108        self.record_call_create(&params.gas, cross_space_internal);
109    }
110
111    fn record_call_result(&mut self, result: &FrameResult) {
112        let gas_left =
113            result.as_ref().map_or(U256::zero(), |r| r.gas_left.clone());
114        self.record_return(&gas_left);
115    }
116
117    fn record_create(&mut self, params: &ActionParams) {
118        self.record_call_create(&params.gas, false);
119    }
120
121    fn record_create_result(&mut self, result: &FrameResult) {
122        let gas_left =
123            result.as_ref().map_or(U256::zero(), |r| r.gas_left.clone());
124        self.record_return(&gas_left);
125    }
126}
127
128impl CheckpointTracer for GasMan {}
129impl InternalTransferTracer for GasMan {}
130impl StorageTracer for GasMan {}
131impl OpcodeTracer for GasMan {}
132impl SetAuthTracer for GasMan {}