cfx_execute_helper/observer/
gasman.rs1use 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 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(¶ms.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(¶ms.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 {}