diff options
Diffstat (limited to 'libevmasm/GasMeter.cpp')
-rw-r--r-- | libevmasm/GasMeter.cpp | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp new file mode 100644 index 00000000..93583169 --- /dev/null +++ b/libevmasm/GasMeter.cpp @@ -0,0 +1,217 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + 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. + + cpp-ethereum is distributed in the hope that it will be useful, + 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file GasMeter.cpp + * @author Christian <c@ethdev.com> + * @date 2015 + */ + +#include "GasMeter.h" +#include <libevmasm/KnownState.h> +using namespace std; +using namespace dev; +using namespace dev::eth; + +GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other) +{ + if (_other.isInfinite && !isInfinite) + *this = infinite(); + if (isInfinite) + return *this; + bigint v = bigint(value) + _other.value; + if (v > numeric_limits<u256>::max()) + *this = infinite(); + else + value = u256(v); + return *this; +} + +GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) +{ + GasConsumption gas; + switch (_item.type()) + { + case Push: + case PushTag: + case PushData: + case PushString: + case PushSub: + case PushSubSize: + case PushProgramSize: + case PushLibraryAddress: + gas = runGas(Instruction::PUSH1); + break; + case Tag: + gas = runGas(Instruction::JUMPDEST); + break; + case Operation: + { + ExpressionClasses& classes = m_state->expressionClasses(); + gas = runGas(_item.instruction()); + 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)) + )) + gas += m_schedule.sstoreResetGas; //@todo take refunds into account + else + gas += m_schedule.sstoreSetGas; + break; + } + case Instruction::SLOAD: + gas += m_schedule.sloadGas; + break; + case Instruction::RETURN: + gas += memoryGas(0, -1); + break; + case Instruction::MLOAD: + case Instruction::MSTORE: + gas += memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(0), + classes.find(AssemblyItem(32)) + })); + break; + case Instruction::MSTORE8: + gas += memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(0), + classes.find(AssemblyItem(1)) + })); + break; + case Instruction::SHA3: + gas = m_schedule.sha3Gas; + gas += wordGas(m_schedule.sha3WordGas, m_state->relativeStackElement(-1)); + gas += memoryGas(0, -1); + break; + case Instruction::CALLDATACOPY: + case Instruction::CODECOPY: + gas += memoryGas(0, -2); + gas += wordGas(m_schedule.copyGas, m_state->relativeStackElement(-2)); + break; + case Instruction::EXTCODECOPY: + gas += memoryGas(-1, -3); + gas += wordGas(m_schedule.copyGas, m_state->relativeStackElement(-3)); + break; + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + { + unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0); + gas = m_schedule.logGas + m_schedule.logTopicGas * n; + gas += memoryGas(0, -1); + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += m_schedule.logDataGas * (*value); + else + gas = GasConsumption::infinite(); + break; + } + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + { + gas = m_schedule.callGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) + gas += (*value); + else + gas = GasConsumption::infinite(); + if (_item.instruction() == Instruction::CALL) + gas += m_schedule.callNewAccountGas; // We very rarely know whether the address exists. + int valueSize = _item.instruction() == Instruction::DELEGATECALL ? 0 : 1; + if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize))) + gas += m_schedule.callValueTransferGas; + gas += memoryGas(-2 - valueSize, -3 - valueSize); + gas += memoryGas(-4 - valueSize, -5 - valueSize); + break; + } + case Instruction::CREATE: + gas = m_schedule.createGas; + gas += memoryGas(-1, -2); + break; + case Instruction::EXP: + gas = m_schedule.expGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += m_schedule.expByteGas * (32 - (h256(*value).firstBitSet() / 8)); + else + gas += m_schedule.expByteGas * 32; + break; + default: + break; + } + break; + } + default: + gas = GasConsumption::infinite(); + break; + } + + m_state->feedItem(_item); + return gas; +} + +GasMeter::GasConsumption GasMeter::wordGas(u256 const& _multiplier, ExpressionClasses::Id _position) +{ + u256 const* value = m_state->expressionClasses().knownConstant(_position); + 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) + return GasConsumption(u256(0)); + u256 previous = m_largestMemoryAccess; + m_largestMemoryAccess = *value; + auto memGas = [=](u256 const& pos) -> u256 + { + u256 size = (pos + 31) / 32; + return m_schedule.memoryGas * size + size * size / m_schedule.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(eth::Instruction::ADD, { + m_state->relativeStackElement(_stackPosOffset), + m_state->relativeStackElement(_stackPosSize) + })); +} + +u256 GasMeter::runGas(Instruction _instruction, EVMSchedule const& _es) +{ + if (_instruction == Instruction::JUMPDEST) + return 1; + + int tier = instructionInfo(_instruction).gasPriceTier; + assertThrow(tier != InvalidTier, OptimizerException, "Invalid gas tier."); + return _es.tierStepGas[tier]; +} + + |