Coverage Report

Created: 2022-08-24 06:52

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