/src/solidity/libevmasm/GasMeter.cpp
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 | | |
19 | | #include <libevmasm/GasMeter.h> |
20 | | |
21 | | #include <libevmasm/KnownState.h> |
22 | | |
23 | | using namespace std; |
24 | | using namespace solidity; |
25 | | using namespace solidity::util; |
26 | | using namespace solidity::evmasm; |
27 | | |
28 | | GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other) |
29 | 0 | { |
30 | 0 | if (_other.isInfinite && !isInfinite) |
31 | 0 | *this = infinite(); |
32 | 0 | if (isInfinite) |
33 | 0 | return *this; |
34 | 0 | bigint v = bigint(value) + _other.value; |
35 | 0 | if (v > numeric_limits<u256>::max()) |
36 | 0 | *this = infinite(); |
37 | 0 | else |
38 | 0 | value = u256(v); |
39 | 0 | return *this; |
40 | 0 | } |
41 | | |
42 | | GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _includeExternalCosts) |
43 | 0 | { |
44 | 0 | GasConsumption gas; |
45 | 0 | switch (_item.type()) |
46 | 0 | { |
47 | 0 | case Push: |
48 | 0 | case PushTag: |
49 | 0 | case PushData: |
50 | 0 | case PushSub: |
51 | 0 | case PushSubSize: |
52 | 0 | case PushProgramSize: |
53 | 0 | case PushLibraryAddress: |
54 | 0 | case PushDeployTimeAddress: |
55 | 0 | gas = runGas(Instruction::PUSH1); |
56 | 0 | break; |
57 | 0 | case Tag: |
58 | 0 | gas = runGas(Instruction::JUMPDEST); |
59 | 0 | break; |
60 | 0 | case Operation: |
61 | 0 | { |
62 | 0 | ExpressionClasses& classes = m_state->expressionClasses(); |
63 | 0 | switch (_item.instruction()) |
64 | 0 | { |
65 | 0 | case Instruction::SSTORE: |
66 | 0 | { |
67 | 0 | ExpressionClasses::Id slot = m_state->relativeStackElement(0); |
68 | 0 | ExpressionClasses::Id value = m_state->relativeStackElement(-1); |
69 | 0 | if (classes.knownZero(value) || ( |
70 | 0 | m_state->storageContent().count(slot) && |
71 | 0 | classes.knownNonZero(m_state->storageContent().at(slot)) |
72 | 0 | )) |
73 | 0 | gas = GasCosts::totalSstoreResetGas(m_evmVersion); //@todo take refunds into account |
74 | 0 | else |
75 | 0 | gas = GasCosts::totalSstoreSetGas(m_evmVersion); |
76 | 0 | break; |
77 | 0 | } |
78 | 0 | case Instruction::SLOAD: |
79 | 0 | gas = GasCosts::sloadGas(m_evmVersion); |
80 | 0 | break; |
81 | 0 | case Instruction::RETURN: |
82 | 0 | case Instruction::REVERT: |
83 | 0 | gas = runGas(_item.instruction()); |
84 | 0 | gas += memoryGas(0, -1); |
85 | 0 | break; |
86 | 0 | case Instruction::MLOAD: |
87 | 0 | case Instruction::MSTORE: |
88 | 0 | gas = runGas(_item.instruction()); |
89 | 0 | gas += memoryGas(classes.find(Instruction::ADD, { |
90 | 0 | m_state->relativeStackElement(0), |
91 | 0 | classes.find(AssemblyItem(32)) |
92 | 0 | })); |
93 | 0 | break; |
94 | 0 | case Instruction::MSTORE8: |
95 | 0 | gas = runGas(_item.instruction()); |
96 | 0 | gas += memoryGas(classes.find(Instruction::ADD, { |
97 | 0 | m_state->relativeStackElement(0), |
98 | 0 | classes.find(AssemblyItem(1)) |
99 | 0 | })); |
100 | 0 | break; |
101 | 0 | case Instruction::KECCAK256: |
102 | 0 | gas = GasCosts::keccak256Gas; |
103 | 0 | gas += memoryGas(0, -1); |
104 | 0 | gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1)); |
105 | 0 | break; |
106 | 0 | case Instruction::CALLDATACOPY: |
107 | 0 | case Instruction::CODECOPY: |
108 | 0 | case Instruction::RETURNDATACOPY: |
109 | 0 | gas = runGas(_item.instruction()); |
110 | 0 | gas += memoryGas(0, -2); |
111 | 0 | gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); |
112 | 0 | break; |
113 | 0 | case Instruction::EXTCODESIZE: |
114 | 0 | gas = GasCosts::extCodeGas(m_evmVersion); |
115 | 0 | break; |
116 | 0 | case Instruction::EXTCODEHASH: |
117 | 0 | gas = GasCosts::balanceGas(m_evmVersion); |
118 | 0 | break; |
119 | 0 | case Instruction::EXTCODECOPY: |
120 | 0 | gas = GasCosts::extCodeGas(m_evmVersion); |
121 | 0 | gas += memoryGas(-1, -3); |
122 | 0 | gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3)); |
123 | 0 | break; |
124 | 0 | case Instruction::LOG0: |
125 | 0 | case Instruction::LOG1: |
126 | 0 | case Instruction::LOG2: |
127 | 0 | case Instruction::LOG3: |
128 | 0 | case Instruction::LOG4: |
129 | 0 | { |
130 | 0 | gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction()); |
131 | 0 | gas += memoryGas(0, -1); |
132 | 0 | if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) |
133 | 0 | gas += GasCosts::logDataGas * (*value); |
134 | 0 | else |
135 | 0 | gas = GasConsumption::infinite(); |
136 | 0 | break; |
137 | 0 | } |
138 | 0 | case Instruction::CALL: |
139 | 0 | case Instruction::CALLCODE: |
140 | 0 | case Instruction::DELEGATECALL: |
141 | 0 | case Instruction::STATICCALL: |
142 | 0 | { |
143 | 0 | if (_includeExternalCosts) |
144 | | // We assume that we do not know the target contract and thus, the consumption is infinite. |
145 | 0 | gas = GasConsumption::infinite(); |
146 | 0 | else |
147 | 0 | { |
148 | 0 | gas = GasCosts::callGas(m_evmVersion); |
149 | 0 | if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) |
150 | 0 | gas += (*value); |
151 | 0 | else |
152 | 0 | gas = GasConsumption::infinite(); |
153 | 0 | if (_item.instruction() == Instruction::CALL) |
154 | 0 | gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists. |
155 | 0 | int valueSize = 1; |
156 | 0 | if (_item.instruction() == Instruction::DELEGATECALL || _item.instruction() == Instruction::STATICCALL) |
157 | 0 | valueSize = 0; |
158 | 0 | else if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize))) |
159 | 0 | gas += GasCosts::callValueTransferGas; |
160 | 0 | gas += memoryGas(-2 - valueSize, -3 - valueSize); |
161 | 0 | gas += memoryGas(-4 - valueSize, -5 - valueSize); |
162 | 0 | } |
163 | 0 | break; |
164 | 0 | } |
165 | 0 | case Instruction::SELFDESTRUCT: |
166 | 0 | gas = GasCosts::selfdestructGas(m_evmVersion); |
167 | 0 | gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists. |
168 | 0 | break; |
169 | 0 | case Instruction::CREATE: |
170 | 0 | case Instruction::CREATE2: |
171 | 0 | if (_includeExternalCosts) |
172 | | // We assume that we do not know the target contract and thus, the consumption is infinite. |
173 | 0 | gas = GasConsumption::infinite(); |
174 | 0 | else |
175 | 0 | { |
176 | 0 | gas = GasCosts::createGas; |
177 | 0 | gas += memoryGas(-1, -2); |
178 | 0 | } |
179 | 0 | break; |
180 | 0 | case Instruction::EXP: |
181 | 0 | gas = GasCosts::expGas; |
182 | 0 | if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) |
183 | 0 | { |
184 | 0 | if (*value) |
185 | 0 | { |
186 | | // Note: msb() counts from 0 and throws on 0 as input. |
187 | 0 | unsigned const significantByteCount = (static_cast<unsigned>(boost::multiprecision::msb(*value)) + 1u + 7u) / 8u; |
188 | 0 | gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | else |
192 | 0 | gas += GasCosts::expByteGas(m_evmVersion) * 32; |
193 | 0 | break; |
194 | 0 | case Instruction::BALANCE: |
195 | 0 | gas = GasCosts::balanceGas(m_evmVersion); |
196 | 0 | break; |
197 | 0 | case Instruction::CHAINID: |
198 | 0 | gas = runGas(Instruction::CHAINID); |
199 | 0 | break; |
200 | 0 | case Instruction::SELFBALANCE: |
201 | 0 | gas = runGas(Instruction::SELFBALANCE); |
202 | 0 | break; |
203 | 0 | default: |
204 | 0 | gas = runGas(_item.instruction()); |
205 | 0 | break; |
206 | 0 | } |
207 | 0 | break; |
208 | 0 | } |
209 | 0 | default: |
210 | 0 | gas = GasConsumption::infinite(); |
211 | 0 | break; |
212 | 0 | } |
213 | | |
214 | 0 | m_state->feedItem(_item); |
215 | 0 | return gas; |
216 | 0 | } |
217 | | |
218 | | GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _value) |
219 | 0 | { |
220 | 0 | u256 const* value = m_state->expressionClasses().knownConstant(_value); |
221 | 0 | if (!value) |
222 | 0 | return GasConsumption::infinite(); |
223 | 0 | return GasConsumption(_multiplier * ((*value + 31) / 32)); |
224 | 0 | } |
225 | | |
226 | | GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position) |
227 | 0 | { |
228 | 0 | u256 const* value = m_state->expressionClasses().knownConstant(_position); |
229 | 0 | if (!value) |
230 | 0 | return GasConsumption::infinite(); |
231 | 0 | if (*value < m_largestMemoryAccess) |
232 | 0 | return GasConsumption(0); |
233 | 0 | u256 previous = m_largestMemoryAccess; |
234 | 0 | m_largestMemoryAccess = *value; |
235 | 0 | auto memGas = [=](u256 const& pos) -> u256 |
236 | 0 | { |
237 | 0 | u256 size = (pos + 31) / 32; |
238 | 0 | return GasCosts::memoryGas * size + size * size / GasCosts::quadCoeffDiv; |
239 | 0 | }; |
240 | 0 | return memGas(*value) - memGas(previous); |
241 | 0 | } |
242 | | |
243 | | GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosSize) |
244 | 0 | { |
245 | 0 | ExpressionClasses& classes = m_state->expressionClasses(); |
246 | 0 | if (classes.knownZero(m_state->relativeStackElement(_stackPosSize))) |
247 | 0 | return GasConsumption(0); |
248 | 0 | else |
249 | 0 | return memoryGas(classes.find(Instruction::ADD, { |
250 | 0 | m_state->relativeStackElement(_stackPosOffset), |
251 | 0 | m_state->relativeStackElement(_stackPosSize) |
252 | 0 | })); |
253 | 0 | } |
254 | | |
255 | | unsigned GasMeter::runGas(Instruction _instruction) |
256 | 1.09M | { |
257 | 1.09M | if (_instruction == Instruction::JUMPDEST) |
258 | 0 | return 1; |
259 | | |
260 | 1.09M | switch (instructionInfo(_instruction).gasPriceTier) |
261 | 1.09M | { |
262 | 0 | case Tier::Zero: return GasCosts::tier0Gas; |
263 | 103k | case Tier::Base: return GasCosts::tier1Gas; |
264 | 955k | case Tier::VeryLow: return GasCosts::tier2Gas; |
265 | 31.7k | case Tier::Low: return GasCosts::tier3Gas; |
266 | 0 | case Tier::Mid: return GasCosts::tier4Gas; |
267 | 0 | case Tier::High: return GasCosts::tier5Gas; |
268 | 0 | case Tier::Ext: return GasCosts::tier6Gas; |
269 | 0 | default: break; |
270 | 1.09M | } |
271 | 0 | assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name); |
272 | 0 | return 0; |
273 | 0 | } |
274 | | |
275 | | u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion) |
276 | 358k | { |
277 | 358k | bigint gas = 0; |
278 | 358k | if (_inCreation) |
279 | 0 | { |
280 | 0 | for (auto b: _data) |
281 | 0 | gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas; |
282 | 0 | } |
283 | 358k | else |
284 | 358k | gas = bigint(GasCosts::createDataGas) * _data.size(); |
285 | 358k | assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits."); |
286 | 358k | return u256(gas); |
287 | 358k | } |
288 | | |
289 | | |
290 | | u256 GasMeter::dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion) |
291 | 0 | { |
292 | 0 | bigint gas = bigint(_length) * (_inCreation ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::createDataGas); |
293 | 0 | assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits."); |
294 | 0 | return u256(gas); |
295 | 0 | } |