aboutsummaryrefslogtreecommitdiffstats
path: root/GasMeter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'GasMeter.cpp')
-rw-r--r--GasMeter.cpp132
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)