diff options
Diffstat (limited to 'GasMeter.cpp')
-rw-r--r-- | GasMeter.cpp | 132 |
1 files changed, 120 insertions, 12 deletions
diff --git a/GasMeter.cpp b/GasMeter.cpp index e5fb0e09..a8dc4dd5 100644 --- a/GasMeter.cpp +++ b/GasMeter.cpp @@ -20,6 +20,7 @@ */ #include "GasMeter.h" +#include <libevmasm/KnownState.h> #include <libevmcore/Params.h> using namespace std; @@ -41,55 +42,162 @@ GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption co GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) { - switch (_item.type()) { + GasConsumption gas; + switch (_item.type()) + { case Push: case PushTag: - return runGas(Instruction::PUSH1); + case PushData: + case PushString: + case PushSub: + case PushSubSize: + case PushProgramSize: + gas = runGas(Instruction::PUSH1); + break; case Tag: - return runGas(Instruction::JUMPDEST); + gas = runGas(Instruction::JUMPDEST); + break; case Operation: { - GasConsumption gas = runGas(_item.instruction()); + ExpressionClasses& classes = m_state->expressionClasses(); + gas = runGas(_item.instruction()); switch (_item.instruction()) { case Instruction::SSTORE: - // @todo logic can be improved - gas += c_sstoreSetGas; + { + 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 += c_sstoreResetGas; //@todo take refunds into account + else + gas += c_sstoreSetGas; break; + } case Instruction::SLOAD: gas += c_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: - case Instruction::MLOAD: - case Instruction::RETURN: + gas += memoryGas(classes.find(eth::Instruction::ADD, { + m_state->relativeStackElement(0), + classes.find(AssemblyItem(1)) + })); + break; case Instruction::SHA3: + gas = c_sha3Gas; + gas += wordGas(c_sha3WordGas, m_state->relativeStackElement(-1)); + gas += memoryGas(0, -1); + break; case Instruction::CALLDATACOPY: case Instruction::CODECOPY: + gas += memoryGas(0, -2); + gas += wordGas(c_copyGas, m_state->relativeStackElement(-2)); + break; case Instruction::EXTCODECOPY: + gas += memoryGas(-1, -3); + gas += wordGas(c_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 = c_logGas + c_logTopicGas * n; + gas += memoryGas(0, -1); + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += c_logDataGas * (*value); + else + gas = GasConsumption::infinite(); + break; + } case Instruction::CALL: case Instruction::CALLCODE: + gas = c_callGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0))) + gas += (*value); + else + gas = GasConsumption::infinite(); + if (_item.instruction() != Instruction::CALLCODE) + gas += c_callNewAccountGas; // We very rarely know whether the address exists. + if (!classes.knownZero(m_state->relativeStackElement(-2))) + gas += c_callValueTransferGas; + gas += memoryGas(-3, -4); + gas += memoryGas(-5, -6); + break; case Instruction::CREATE: + gas = c_createGas; + gas += memoryGas(-1, -2); + break; case Instruction::EXP: - // @todo logic can be improved - gas = GasConsumption::infinite(); + gas = c_expGas; + if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) + gas += c_expByteGas * (32 - (h256(*value).firstBitSet() / 8)); + else + gas = GasConsumption::infinite(); break; default: break; } - return gas; break; } default: + gas = GasConsumption::infinite(); break; } - return GasConsumption::infinite(); + 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 c_memoryGas * size + size * size / c_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) + })); } GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction) |