2015-05-06 08:43:59 +00:00
|
|
|
/*
|
2016-11-18 23:13:20 +00:00
|
|
|
This file is part of solidity.
|
2015-05-06 08:43:59 +00:00
|
|
|
|
2016-11-18 23:13:20 +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.
|
|
|
|
|
2016-11-18 23:13:20 +00:00
|
|
|
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
|
2016-11-18 23:13:20 +00:00
|
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
2015-05-06 08:43:59 +00:00
|
|
|
*/
|
|
|
|
|
2017-07-13 09:33:06 +00:00
|
|
|
#include <libevmasm/GasMeter.h>
|
|
|
|
|
2015-05-19 22:27:07 +00:00
|
|
|
#include <libevmasm/KnownState.h>
|
2017-07-13 09:33:06 +00:00
|
|
|
|
2015-05-06 08:43:59 +00:00
|
|
|
using namespace std;
|
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;
|
2015-05-26 09:29:41 +00:00
|
|
|
if (v > 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
|
|
|
{
|
2015-05-19 22:27:07 +00:00
|
|
|
GasConsumption gas;
|
|
|
|
switch (_item.type())
|
|
|
|
{
|
2015-05-06 08:43:59 +00:00
|
|
|
case Push:
|
|
|
|
case PushTag:
|
2015-05-19 22:27:07 +00:00
|
|
|
case PushData:
|
|
|
|
case PushString:
|
|
|
|
case PushSub:
|
|
|
|
case PushSubSize:
|
|
|
|
case PushProgramSize:
|
2015-09-10 10:02:18 +00:00
|
|
|
case PushLibraryAddress:
|
2017-11-14 11:58:04 +00:00
|
|
|
case PushDeployTimeAddress:
|
2015-05-19 22:27:07 +00:00
|
|
|
gas = runGas(Instruction::PUSH1);
|
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
case Tag:
|
2015-05-19 22:27:07 +00:00
|
|
|
gas = runGas(Instruction::JUMPDEST);
|
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
case Operation:
|
|
|
|
{
|
2015-05-19 22:27:07 +00:00
|
|
|
ExpressionClasses& classes = m_state->expressionClasses();
|
2015-05-06 08:43:59 +00:00
|
|
|
switch (_item.instruction())
|
|
|
|
{
|
|
|
|
case Instruction::SSTORE:
|
2015-05-19 22:27:07 +00:00
|
|
|
{
|
|
|
|
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))
|
|
|
|
))
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = GasCosts::sstoreResetGas; //@todo take refunds into account
|
2015-05-19 22:27:07 +00:00
|
|
|
else
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = GasCosts::sstoreSetGas;
|
2015-05-06 08:43:59 +00:00
|
|
|
break;
|
2015-05-19 22:27:07 +00:00
|
|
|
}
|
2015-05-06 08:43:59 +00:00
|
|
|
case Instruction::SLOAD:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = GasCosts::sloadGas(m_evmVersion);
|
2015-05-06 08:43:59 +00:00
|
|
|
break;
|
2015-05-19 22:27:07 +00:00
|
|
|
case Instruction::RETURN:
|
2017-02-06 20:21:27 +00:00
|
|
|
case Instruction::REVERT:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = runGas(_item.instruction());
|
2015-05-19 22:27:07 +00:00
|
|
|
gas += memoryGas(0, -1);
|
|
|
|
break;
|
|
|
|
case Instruction::MLOAD:
|
2015-05-06 08:43:59 +00:00
|
|
|
case Instruction::MSTORE:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = runGas(_item.instruction());
|
2016-04-04 11:41:35 +00:00
|
|
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
2015-05-19 22:27:07 +00:00
|
|
|
m_state->relativeStackElement(0),
|
|
|
|
classes.find(AssemblyItem(32))
|
|
|
|
}));
|
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
case Instruction::MSTORE8:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = runGas(_item.instruction());
|
2016-04-04 11:41:35 +00:00
|
|
|
gas += memoryGas(classes.find(Instruction::ADD, {
|
2015-05-19 22:27:07 +00:00
|
|
|
m_state->relativeStackElement(0),
|
|
|
|
classes.find(AssemblyItem(1))
|
|
|
|
}));
|
|
|
|
break;
|
2017-05-10 07:48:00 +00:00
|
|
|
case Instruction::KECCAK256:
|
|
|
|
gas = GasCosts::keccak256Gas;
|
2015-05-19 22:27:07 +00:00
|
|
|
gas += memoryGas(0, -1);
|
2018-09-18 19:46:25 +00:00
|
|
|
gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1));
|
2015-05-19 22:27:07 +00:00
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
case Instruction::CALLDATACOPY:
|
|
|
|
case Instruction::CODECOPY:
|
2017-05-17 11:29:42 +00:00
|
|
|
case Instruction::RETURNDATACOPY:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = runGas(_item.instruction());
|
2015-05-19 22:27:07 +00:00
|
|
|
gas += memoryGas(0, -2);
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2));
|
2015-05-19 22:27:07 +00:00
|
|
|
break;
|
2018-03-01 11:06:36 +00:00
|
|
|
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:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = GasCosts::extCodeGas(m_evmVersion);
|
2015-05-19 22:27:07 +00:00
|
|
|
gas += memoryGas(-1, -3);
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-3));
|
2015-05-19 22:27:07 +00:00
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
case Instruction::LOG0:
|
|
|
|
case Instruction::LOG1:
|
|
|
|
case Instruction::LOG2:
|
|
|
|
case Instruction::LOG3:
|
|
|
|
case Instruction::LOG4:
|
2015-05-19 22:27:07 +00:00
|
|
|
{
|
2018-09-18 20:25:52 +00:00
|
|
|
gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction());
|
2015-05-19 22:27:07 +00:00
|
|
|
gas += memoryGas(0, -1);
|
|
|
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
2016-04-06 18:55:46 +00:00
|
|
|
gas += GasCosts::logDataGas * (*value);
|
2015-05-19 22:27:07 +00:00
|
|
|
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:
|
2017-06-08 14:44:03 +00:00
|
|
|
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
|
|
|
|
{
|
2018-03-01 11:06:36 +00:00
|
|
|
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.
|
2017-06-08 14:44:03 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-05-19 22:27:07 +00:00
|
|
|
break;
|
2016-03-03 15:56:22 +00:00
|
|
|
}
|
2017-01-16 13:49:14 +00:00
|
|
|
case Instruction::SELFDESTRUCT:
|
2018-03-01 11:06:36 +00:00
|
|
|
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:
|
2016-04-06 18:55:46 +00:00
|
|
|
gas = GasCosts::expGas;
|
2015-05-19 22:27:07 +00:00
|
|
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
|
2019-12-13 00:35:55 +00:00
|
|
|
{
|
|
|
|
if (*value)
|
|
|
|
{
|
|
|
|
// Note: msb() counts from 0 and throws on 0 as input.
|
|
|
|
unsigned const significantByteCount = (boost::multiprecision::msb(*value) + 1 + 7) / 8;
|
|
|
|
gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount;
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 22:27:07 +00:00
|
|
|
else
|
2018-03-01 11:06:36 +00:00
|
|
|
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);
|
|
|
|
break;
|
|
|
|
case Instruction::SELFBALANCE:
|
|
|
|
gas = runGas(Instruction::SELFBALANCE);
|
|
|
|
break;
|
2015-05-06 08:43:59 +00:00
|
|
|
default:
|
2018-03-01 11:06:36 +00:00
|
|
|
gas = runGas(_item.instruction());
|
2015-05-06 08:43:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2015-05-19 22:27:07 +00:00
|
|
|
gas = GasConsumption::infinite();
|
2015-05-06 08:43:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-05-19 22:27:07 +00:00
|
|
|
m_state->feedItem(_item);
|
|
|
|
return gas;
|
|
|
|
}
|
|
|
|
|
2017-08-22 17:24:02 +00:00
|
|
|
GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _value)
|
2015-05-19 22:27:07 +00:00
|
|
|
{
|
2017-08-22 17:24:02 +00:00
|
|
|
u256 const* value = m_state->expressionClasses().knownConstant(_value);
|
2015-05-19 22:27:07 +00:00
|
|
|
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);
|
2015-05-19 22:27:07 +00:00
|
|
|
u256 previous = m_largestMemoryAccess;
|
|
|
|
m_largestMemoryAccess = *value;
|
2015-11-21 13:33:55 +00:00
|
|
|
auto memGas = [=](u256 const& pos) -> u256
|
2015-05-19 22:27:07 +00:00
|
|
|
{
|
|
|
|
u256 size = (pos + 31) / 32;
|
2016-04-06 18:55:46 +00:00
|
|
|
return GasCosts::memoryGas * size + size * size / GasCosts::quadCoeffDiv;
|
2015-05-19 22:27:07 +00:00
|
|
|
};
|
|
|
|
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
|
2016-04-04 11:41:35 +00:00
|
|
|
return memoryGas(classes.find(Instruction::ADD, {
|
2015-05-19 22:27:07 +00:00
|
|
|
m_state->relativeStackElement(_stackPosOffset),
|
|
|
|
m_state->relativeStackElement(_stackPosSize)
|
|
|
|
}));
|
2015-05-06 08:43:59 +00:00
|
|
|
}
|
|
|
|
|
2016-04-06 18:55:46 +00:00
|
|
|
unsigned GasMeter::runGas(Instruction _instruction)
|
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
|
|
|
|
2016-04-06 18:55:46 +00:00
|
|
|
switch (instructionInfo(_instruction).gasPriceTier)
|
|
|
|
{
|
2017-01-18 16:24:39 +00:00
|
|
|
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;
|
2016-04-06 18:55:46 +00:00
|
|
|
default: break;
|
|
|
|
}
|
2018-03-01 11:06:36 +00:00
|
|
|
assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction).name);
|
2016-04-06 18:55:46 +00:00
|
|
|
return 0;
|
2015-05-06 08:43:59 +00:00
|
|
|
}
|
|
|
|
|
2019-11-07 14:10:08 +00:00
|
|
|
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion)
|
2018-08-08 22:13:28 +00:00
|
|
|
{
|
|
|
|
bigint gas = 0;
|
|
|
|
if (_inCreation)
|
|
|
|
{
|
|
|
|
for (auto b: _data)
|
2019-11-07 14:10:08 +00:00
|
|
|
gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas;
|
2018-08-08 22:13:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
gas = bigint(GasCosts::createDataGas) * _data.size();
|
|
|
|
assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits.");
|
|
|
|
return u256(gas);
|
|
|
|
}
|