Coverage Report

Created: 2022-08-24 06:40

/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
}