solidity/libevmasm/GasMeter.cpp

301 lines
9.3 KiB
C++
Raw Normal View History

2015-05-06 08:43:59 +00:00
/*
This file is part of solidity.
2015-05-06 08:43:59 +00:00
solidity is free software: you can redistribute it and/or modify
2015-05-06 08:43:59 +00:00
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
2015-05-06 08:43:59 +00:00
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
2015-05-06 08:43:59 +00:00
*/
// SPDX-License-Identifier: GPL-3.0
2015-05-06 08:43:59 +00:00
#include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h>
2019-12-11 16:31:36 +00:00
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
2015-05-06 08:43:59 +00:00
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
{
2015-05-22 07:33:57 +00:00
if (_other.isInfinite && !isInfinite)
*this = infinite();
2015-05-06 08:43:59 +00:00
if (isInfinite)
return *this;
bigint v = bigint(value) + _other.value;
2023-07-10 11:12:46 +00:00
if (v > std::numeric_limits<u256>::max())
2015-05-22 07:33:57 +00:00
*this = infinite();
2015-05-06 08:43:59 +00:00
else
value = u256(v);
return *this;
}
2016-11-14 22:28:26 +00:00
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _includeExternalCosts)
2015-05-06 08:43:59 +00:00
{
GasConsumption gas;
switch (_item.type())
{
2015-05-06 08:43:59 +00:00
case Push:
if (m_evmVersion.hasPush0() && _item.data() == 0)
{
gas = runGas(Instruction::PUSH0, m_evmVersion);
break;
}
[[fallthrough]];
2015-05-06 08:43:59 +00:00
case PushTag:
case PushData:
case PushSub:
case PushSubSize:
case PushProgramSize:
case PushLibraryAddress:
2017-11-14 11:58:04 +00:00
case PushDeployTimeAddress:
gas = runGas(Instruction::PUSH1, m_evmVersion);
break;
2015-05-06 08:43:59 +00:00
case Tag:
gas = runGas(Instruction::JUMPDEST, m_evmVersion);
break;
2015-05-06 08:43:59 +00:00
case Operation:
{
ExpressionClasses& classes = m_state->expressionClasses();
2015-05-06 08:43:59 +00:00
switch (_item.instruction())
{
case Instruction::SSTORE:
{
ExpressionClasses::Id slot = m_state->relativeStackElement(0);
ExpressionClasses::Id value = m_state->relativeStackElement(-1);
if (classes.knownZero(value) || (
m_state->storageContent().count(slot) &&
classes.knownNonZero(m_state->storageContent().at(slot))
))
2021-04-13 07:15:32 +00:00
gas = GasCosts::totalSstoreResetGas(m_evmVersion); //@todo take refunds into account
else
2021-04-13 07:15:32 +00:00
gas = GasCosts::totalSstoreSetGas(m_evmVersion);
2015-05-06 08:43:59 +00:00
break;
}
2015-05-06 08:43:59 +00:00
case Instruction::SLOAD:
gas = GasCosts::sloadGas(m_evmVersion);
2015-05-06 08:43:59 +00:00
break;
case Instruction::RETURN:
2017-02-06 20:21:27 +00:00
case Instruction::REVERT:
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -1);
break;
case Instruction::MLOAD:
2015-05-06 08:43:59 +00:00
case Instruction::MSTORE:
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(32))
}));
break;
2015-05-06 08:43:59 +00:00
case Instruction::MSTORE8:
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(0),
classes.find(AssemblyItem(1))
}));
break;
case Instruction::KECCAK256:
gas = GasCosts::keccak256Gas;
gas += memoryGas(0, -1);
2018-09-18 19:46:25 +00:00
gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1));
break;
2015-05-06 08:43:59 +00:00
case Instruction::CALLDATACOPY:
case Instruction::CODECOPY:
case Instruction::RETURNDATACOPY:
gas = runGas(_item.instruction(), m_evmVersion);
gas += memoryGas(0, -2);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
break;
case Instruction::EXTCODESIZE:
gas = GasCosts::extCodeGas(m_evmVersion);
break;
2018-09-25 21:49:26 +00:00
case Instruction::EXTCODEHASH:
gas = GasCosts::balanceGas(m_evmVersion);
break;
2015-05-06 08:43:59 +00:00
case Instruction::EXTCODECOPY:
gas = GasCosts::extCodeGas(m_evmVersion);
gas += memoryGas(-1, -3);
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
break;
2015-05-06 08:43:59 +00:00
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
2018-09-18 20:25:52 +00:00
gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction());
gas += memoryGas(0, -1);
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += GasCosts::logDataGas * (*value);
else
gas = GasConsumption::infinite();
break;
}
2015-05-06 08:43:59 +00:00
case Instruction::CALL:
case Instruction::CALLCODE:
2016-03-03 15:56:22 +00:00
case Instruction::DELEGATECALL:
case Instruction::STATICCALL:
2016-03-03 15:56:22 +00:00
{
2016-11-14 22:28:26 +00:00
if (_includeExternalCosts)
// We assume that we do not know the target contract and thus, the consumption is infinite.
gas = GasConsumption::infinite();
else
{
gas = GasCosts::callGas(m_evmVersion);
2016-11-14 22:28:26 +00:00
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
gas += (*value);
else
gas = GasConsumption::infinite();
if (_item.instruction() == Instruction::CALL)
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
int valueSize = 1;
if (_item.instruction() == Instruction::DELEGATECALL || _item.instruction() == Instruction::STATICCALL)
valueSize = 0;
else if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize)))
2016-11-14 22:28:26 +00:00
gas += GasCosts::callValueTransferGas;
gas += memoryGas(-2 - valueSize, -3 - valueSize);
gas += memoryGas(-4 - valueSize, -5 - valueSize);
}
break;
2016-03-03 15:56:22 +00:00
}
case Instruction::SELFDESTRUCT:
gas = GasCosts::selfdestructGas(m_evmVersion);
2017-01-16 13:57:49 +00:00
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
2017-02-09 17:11:23 +00:00
break;
2015-05-06 08:43:59 +00:00
case Instruction::CREATE:
2017-04-20 20:36:53 +00:00
case Instruction::CREATE2:
2016-11-14 22:28:26 +00:00
if (_includeExternalCosts)
// We assume that we do not know the target contract and thus, the consumption is infinite.
gas = GasConsumption::infinite();
else
{
gas = GasCosts::createGas;
gas += memoryGas(-1, -2);
}
break;
2015-05-06 08:43:59 +00:00
case Instruction::EXP:
gas = GasCosts::expGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
{
if (*value)
{
// Note: msb() counts from 0 and throws on 0 as input.
2022-06-15 08:34:33 +00:00
unsigned const significantByteCount = (static_cast<unsigned>(boost::multiprecision::msb(*value)) + 1u + 7u) / 8u;
gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount;
}
}
else
gas += GasCosts::expByteGas(m_evmVersion) * 32;
break;
case Instruction::BALANCE:
gas = GasCosts::balanceGas(m_evmVersion);
2015-05-06 08:43:59 +00:00
break;
2019-09-02 13:23:45 +00:00
case Instruction::CHAINID:
gas = runGas(Instruction::CHAINID, m_evmVersion);
2019-09-02 13:23:45 +00:00
break;
case Instruction::SELFBALANCE:
gas = runGas(Instruction::SELFBALANCE, m_evmVersion);
2019-09-02 13:23:45 +00:00
break;
2015-05-06 08:43:59 +00:00
default:
gas = runGas(_item.instruction(), m_evmVersion);
2015-05-06 08:43:59 +00:00
break;
}
break;
}
default:
gas = GasConsumption::infinite();
2015-05-06 08:43:59 +00:00
break;
}
m_state->feedItem(_item);
return gas;
}
GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _value)
{
u256 const* value = m_state->expressionClasses().knownConstant(_value);
if (!value)
return GasConsumption::infinite();
return GasConsumption(_multiplier * ((*value + 31) / 32));
}
GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position)
{
u256 const* value = m_state->expressionClasses().knownConstant(_position);
if (!value)
return GasConsumption::infinite();
if (*value < m_largestMemoryAccess)
2018-09-18 19:46:25 +00:00
return GasConsumption(0);
u256 previous = m_largestMemoryAccess;
m_largestMemoryAccess = *value;
auto memGas = [=](u256 const& pos) -> u256
{
u256 size = (pos + 31) / 32;
return GasCosts::memoryGas * size + size * size / GasCosts::quadCoeffDiv;
};
return memGas(*value) - memGas(previous);
}
GasMeter::GasConsumption GasMeter::memoryGas(int _stackPosOffset, int _stackPosSize)
{
ExpressionClasses& classes = m_state->expressionClasses();
if (classes.knownZero(m_state->relativeStackElement(_stackPosSize)))
return GasConsumption(0);
else
return memoryGas(classes.find(Instruction::ADD, {
m_state->relativeStackElement(_stackPosOffset),
m_state->relativeStackElement(_stackPosSize)
}));
2015-05-06 08:43:59 +00:00
}
unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVersion)
2015-05-06 08:43:59 +00:00
{
if (_instruction == Instruction::JUMPDEST)
2015-06-01 10:32:59 +00:00
return 1;
2015-05-06 08:43:59 +00:00
switch (instructionInfo(_instruction, _evmVersion).gasPriceTier)
{
case Tier::Zero: return GasCosts::tier0Gas;
case Tier::Base: return GasCosts::tier1Gas;
case Tier::VeryLow: return GasCosts::tier2Gas;
case Tier::Low: return GasCosts::tier3Gas;
case Tier::Mid: return GasCosts::tier4Gas;
case Tier::High: return GasCosts::tier5Gas;
case Tier::Ext: return GasCosts::tier6Gas;
default: break;
}
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name);
return 0;
2015-05-06 08:43:59 +00:00
}
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion)
{
bigint gas = 0;
if (_inCreation)
{
for (auto b: _data)
gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas;
}
else
gas = bigint(GasCosts::createDataGas) * _data.size();
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
return u256(gas);
}
2021-01-14 12:02:14 +00:00
u256 GasMeter::dataGas(uint64_t _length, bool _inCreation, langutil::EVMVersion _evmVersion)
{
bigint gas = bigint(_length) * (_inCreation ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::createDataGas);
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
return u256(gas);
}