/src/solidity/libevmasm/GasMeter.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** @file GasMeter.cpp |
19 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2015 |
21 | | * |
22 | | * Utilities for tracking gas costs. |
23 | | * |
24 | | * With respect to EIP-2929, we do not track warm accounts or storage slots and they are always |
25 | | * charged the worst-case, i.e., cold-access. |
26 | | */ |
27 | | |
28 | | #pragma once |
29 | | |
30 | | #include <libevmasm/ExpressionClasses.h> |
31 | | #include <libevmasm/AssemblyItem.h> |
32 | | |
33 | | #include <liblangutil/EVMVersion.h> |
34 | | |
35 | | #include <ostream> |
36 | | #include <tuple> |
37 | | #include <utility> |
38 | | |
39 | | namespace solidity::evmasm |
40 | | { |
41 | | |
42 | | class KnownState; |
43 | | |
44 | | namespace GasCosts |
45 | | { |
46 | | static unsigned const stackLimit = 1024; |
47 | | static unsigned const tier0Gas = 0; |
48 | | static unsigned const tier1Gas = 2; |
49 | | static unsigned const tier2Gas = 3; |
50 | | static unsigned const tier3Gas = 5; |
51 | | static unsigned const tier4Gas = 8; |
52 | | static unsigned const tier5Gas = 10; |
53 | | static unsigned const tier6Gas = 20; |
54 | | static unsigned const tier7Gas = 0; |
55 | | static unsigned const expGas = 10; |
56 | | inline unsigned expByteGas(langutil::EVMVersion _evmVersion) |
57 | 0 | { |
58 | 0 | return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10; |
59 | 0 | } |
60 | | static unsigned const keccak256Gas = 30; |
61 | | static unsigned const keccak256WordGas = 6; |
62 | | /// Corresponds to ACCESS_LIST_ADDRESS_COST from EIP-2930 |
63 | | static unsigned const accessListAddressCost = 2400; |
64 | | /// Corresponds to ACCESS_LIST_STORAGE_COST from EIP-2930 |
65 | | static unsigned const accessListStorageKeyCost = 1900; |
66 | | /// Corresponds to COLD_SLOAD_COST from EIP-2929 |
67 | | static unsigned const coldSloadCost = 2100; |
68 | | /// Corresponds to COLD_ACCOUNT_ACCESS_COST from EIP-2929 |
69 | | static unsigned const coldAccountAccessCost = 2600; |
70 | | /// Corresponds to WARM_STORAGE_READ_COST from EIP-2929 |
71 | | static unsigned const warmStorageReadCost = 100; |
72 | | inline unsigned sloadGas(langutil::EVMVersion _evmVersion) |
73 | 0 | { |
74 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
75 | 0 | return coldSloadCost; |
76 | 0 | else if (_evmVersion >= langutil::EVMVersion::istanbul()) |
77 | 0 | return 800; |
78 | 0 | else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) |
79 | 0 | return 200; |
80 | 0 | else |
81 | 0 | return 50; |
82 | 0 | } |
83 | | /// Corresponds to SSTORE_SET_GAS |
84 | | static unsigned const sstoreSetGas = 20000; |
85 | | /// Corresponds to SSTORE_RESET_GAS from EIP-2929 |
86 | | static unsigned const sstoreResetGas = 5000 - coldSloadCost; |
87 | | /// Corresponds to SSTORE_CLEARS_SCHEDULE from EIP-2200 |
88 | | inline static unsigned sstoreClearsSchedule(langutil::EVMVersion _evmVersion) |
89 | 0 | { |
90 | 0 | // Changes from EIP-3529 |
91 | 0 | if (_evmVersion >= langutil::EVMVersion::london()) |
92 | 0 | return sstoreResetGas + accessListStorageKeyCost; |
93 | 0 | else |
94 | 0 | return 15000; |
95 | 0 | } Unexecuted instantiation: EVMHost.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: CompilerStack.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: GasEstimator.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: ContractCompiler.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: ExpressionCompiler.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: IRGeneratorForStatements.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: StackLayoutGenerator.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: EVMMetrics.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: LoadResolver.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: Assembly.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: ConstantOptimiser.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: GasMeter.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: Inliner.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) Unexecuted instantiation: PathGasMeter.cpp:solidity::evmasm::GasCosts::sstoreClearsSchedule(solidity::langutil::EVMVersion) |
96 | | inline static unsigned totalSstoreSetGas(langutil::EVMVersion _evmVersion) |
97 | 0 | { |
98 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
99 | 0 | return sstoreSetGas + coldSloadCost; |
100 | 0 | else |
101 | 0 | return sstoreSetGas; |
102 | 0 | } Unexecuted instantiation: EVMHost.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: CompilerStack.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: GasEstimator.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: ContractCompiler.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: ExpressionCompiler.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: IRGeneratorForStatements.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: StackLayoutGenerator.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: EVMMetrics.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: LoadResolver.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: Assembly.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: ConstantOptimiser.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: GasMeter.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: Inliner.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) Unexecuted instantiation: PathGasMeter.cpp:solidity::evmasm::GasCosts::totalSstoreSetGas(solidity::langutil::EVMVersion) |
103 | | /// Corresponds to SSTORE_RESET_GAS from EIP-2929 |
104 | | /// For Berlin, the maximum is SSTORE_RESET_GAS + COLD_SLOAD_COST = 5000 |
105 | | /// For previous versions, it's a fixed 5000 |
106 | | inline unsigned totalSstoreResetGas(langutil::EVMVersion _evmVersion) |
107 | 0 | { |
108 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
109 | 0 | return sstoreResetGas + coldSloadCost; |
110 | 0 | else |
111 | 0 | return 5000; |
112 | 0 | } |
113 | | inline unsigned extCodeGas(langutil::EVMVersion _evmVersion) |
114 | 0 | { |
115 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
116 | 0 | return coldAccountAccessCost; |
117 | 0 | else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) |
118 | 0 | return 700; |
119 | 0 | else |
120 | 0 | return 20; |
121 | 0 | } |
122 | | inline unsigned balanceGas(langutil::EVMVersion _evmVersion) |
123 | 0 | { |
124 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
125 | 0 | return coldAccountAccessCost; |
126 | 0 | else if (_evmVersion >= langutil::EVMVersion::istanbul()) |
127 | 0 | return 700; |
128 | 0 | else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) |
129 | 0 | return 400; |
130 | 0 | else |
131 | 0 | return 20; |
132 | 0 | } |
133 | | static unsigned const jumpdestGas = 1; |
134 | | static unsigned const logGas = 375; |
135 | | static unsigned const logDataGas = 8; |
136 | | static unsigned const logTopicGas = 375; |
137 | | static unsigned const createGas = 32000; |
138 | | inline unsigned callGas(langutil::EVMVersion _evmVersion) |
139 | 0 | { |
140 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
141 | 0 | return coldAccountAccessCost; |
142 | 0 | else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) |
143 | 0 | return 700; |
144 | 0 | else |
145 | 0 | return 40; |
146 | 0 | } |
147 | | static unsigned const callStipend = 2300; |
148 | | static unsigned const callValueTransferGas = 9000; |
149 | | static unsigned const callNewAccountGas = 25000; |
150 | | inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion) |
151 | 0 | { |
152 | 0 | if (_evmVersion >= langutil::EVMVersion::berlin()) |
153 | 0 | return coldAccountAccessCost; |
154 | 0 | else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle()) |
155 | 0 | return 5000; |
156 | 0 | else |
157 | 0 | return 0; |
158 | 0 | } |
159 | | inline unsigned selfdestructRefundGas(langutil::EVMVersion _evmVersion) |
160 | 0 | { |
161 | 0 | // Changes from EIP-3529 |
162 | 0 | if (_evmVersion >= langutil::EVMVersion::london()) |
163 | 0 | return 0; |
164 | 0 | else |
165 | 0 | return 24000; |
166 | 0 | } |
167 | | static unsigned const memoryGas = 3; |
168 | | static unsigned const quadCoeffDiv = 512; |
169 | | static unsigned const createDataGas = 200; |
170 | | static unsigned const txGas = 21000; |
171 | | static unsigned const txCreateGas = 53000; |
172 | | static unsigned const txDataZeroGas = 4; |
173 | | inline unsigned txDataNonZeroGas(langutil::EVMVersion _evmVersion) |
174 | 137M | { |
175 | 137M | return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68; |
176 | 137M | } |
177 | | static unsigned const copyGas = 3; |
178 | | } |
179 | | |
180 | | /** |
181 | | * Class that helps computing the maximum gas consumption for instructions. |
182 | | * Has to be initialized with a certain known state that will be automatically updated for |
183 | | * each call to estimateMax. These calls have to supply strictly subsequent AssemblyItems. |
184 | | * A new gas meter has to be constructed (with a new state) for control flow changes. |
185 | | */ |
186 | | class GasMeter |
187 | | { |
188 | | public: |
189 | | struct GasConsumption |
190 | | { |
191 | 0 | GasConsumption(unsigned _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {} |
192 | 0 | GasConsumption(u256 _value, bool _infinite = false): value(std::move(_value)), isInfinite(_infinite) {} |
193 | 0 | static GasConsumption infinite() { return GasConsumption(0, true); } |
194 | | |
195 | | GasConsumption& operator+=(GasConsumption const& _other); |
196 | | GasConsumption operator+(GasConsumption const& _other) const |
197 | 0 | { |
198 | 0 | GasConsumption result = *this; |
199 | 0 | result += _other; |
200 | 0 | return result; |
201 | 0 | } |
202 | | bool operator<(GasConsumption const& _other) const |
203 | 0 | { |
204 | 0 | return std::make_pair(isInfinite, value) < std::make_pair(_other.isInfinite, _other.value); |
205 | 0 | } |
206 | | |
207 | | u256 value; |
208 | | bool isInfinite; |
209 | | }; |
210 | | |
211 | | /// Constructs a new gas meter given the current state. |
212 | | GasMeter(std::shared_ptr<KnownState> _state, langutil::EVMVersion _evmVersion, u256 _largestMemoryAccess = 0): |
213 | 0 | m_state(std::move(_state)), m_evmVersion(_evmVersion), m_largestMemoryAccess(std::move(_largestMemoryAccess)) {} |
214 | | |
215 | | /// @returns an upper bound on the gas consumed by the given instruction and updates |
216 | | /// the state. |
217 | | /// @param _inculdeExternalCosts if true, include costs caused by other contracts in calls. |
218 | | GasConsumption estimateMax(AssemblyItem const& _item, bool _includeExternalCosts = true); |
219 | | |
220 | 0 | u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; } |
221 | | |
222 | | /// @returns gas costs for simple instructions with constant gas costs (that do not |
223 | | /// change with EVM versions) |
224 | | static unsigned runGas(Instruction _instruction); |
225 | | |
226 | | /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. |
227 | | /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas |
228 | | /// otherwise code will be stored and have to pay "createDataGas" cost. |
229 | | static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion); |
230 | | |
231 | | /// @returns the gas cost of non-zero data of the supplied length, depending whether it is in creation code, or not. |
232 | | /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas |
233 | | /// otherwise code will be stored and have to pay "createDataGas" cost. |
234 | | static u256 dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion); |
235 | | |
236 | | private: |
237 | | /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. |
238 | | GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); |
239 | | /// @returns the gas needed to access the given memory position. |
240 | | /// @todo this assumes that memory was never accessed before and thus over-estimates gas usage. |
241 | | GasConsumption memoryGas(ExpressionClasses::Id _position); |
242 | | /// @returns the memory gas for accessing the memory at a specific offset for a number of bytes |
243 | | /// given as values on the stack at the given relative positions. |
244 | | GasConsumption memoryGas(int _stackPosOffset, int _stackPosSize); |
245 | | |
246 | | std::shared_ptr<KnownState> m_state; |
247 | | langutil::EVMVersion m_evmVersion; |
248 | | /// Largest point where memory was accessed since the creation of this object. |
249 | | u256 m_largestMemoryAccess; |
250 | | }; |
251 | | |
252 | | inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption) |
253 | 0 | { |
254 | 0 | if (_consumption.isInfinite) |
255 | 0 | return _str << "[???]"; |
256 | 0 | else |
257 | 0 | return _str << std::dec << _consumption.value; |
258 | 0 | } |
259 | | |
260 | | |
261 | | } |