Coverage Report

Created: 2026-08-14 07:35

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