Browse Source

Remove gas counter from VM interface (VMFace)

cl-refactor
Paweł Bylica 10 years ago
parent
commit
6cf5976515
  1. 16
      evmjit/libevmjit-cpp/JitVM.cpp
  2. 3
      evmjit/libevmjit-cpp/JitVM.h
  3. 42
      libethereum/Executive.cpp
  4. 8
      libethereum/Executive.h
  5. 4
      libethereum/ExtVM.cpp
  6. 23
      libevm/VM.cpp
  7. 9
      libevm/VM.h
  8. 10
      libevm/VMFace.h
  9. 6
      libevm/VMFactory.cpp
  10. 2
      libevm/VMFactory.h
  11. 8
      test/fuzzTesting/checkRandomVMTest.cpp
  12. 8
      test/fuzzTesting/createRandomVMTest.cpp
  13. 10
      test/libevm/vm.cpp

16
evmjit/libevmjit-cpp/JitVM.cpp

@ -18,29 +18,27 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter";
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
VMFactory::setKind(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas);
m_fallbackVM = VMFactory::create();
VMFactory::setKind(VMKind::JIT);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
return m_fallbackVM->go(io_gas, _ext, _onOp, _step);
}
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas);
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
@ -80,7 +78,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
break;
}
m_gas = m_data.gas; // TODO: Remove m_gas field
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
}

3
evmjit/libevmjit-cpp/JitVM.h

@ -10,11 +10,10 @@ namespace eth
class JitVM: public VMFace
{
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;

42
libethereum/Executive.cpp

@ -41,7 +41,7 @@ Executive::Executive(State& _s, BlockChain const& _bc, unsigned _level):
u256 Executive::gasUsed() const
{
return m_t.gas() - m_endGas;
return m_t.gas() - m_gas;
}
ExecutionResult Executive::executionResult() const
@ -133,26 +133,27 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen
bigint g = it->second.gas(_data);
if (_gas < g)
{
m_endGas = 0;
m_excepted = TransactionException::OutOfGasBase;
// Bail from exception.
return true; // true actually means "all finished - nothing more to be done regarding go().
}
else
{
m_endGas = (u256)(_gas - g);
m_gas = (u256)(_gas - g);
m_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
else
{
m_vm = VMFactory::create(_gas);
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
m_gas = _gas;
if (m_s.addressHasCode(_codeAddress))
{
m_vm = VMFactory::create();
bytes const& c = m_s.code(_codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _receiveAddress, _senderAddress, _originAddress, _value, _gasPrice, _data, &c, m_depth);
}
}
else
m_endGas = _gas;
m_s.transferBalance(_senderAddress, _receiveAddress, _value);
@ -166,11 +167,12 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
// We can allow for the reverted state (i.e. that with which m_ext is constructed) to contain the m_newAddress, since
// we delete it explicitly if we decide we need to revert.
m_newAddress = right160(sha3(rlpList(_sender, m_s.transactionsFrom(_sender) - 1)));
m_gas = _gas;
// Execute _init.
if (!_init.empty())
{
m_vm = VMFactory::create(_gas);
m_vm = VMFactory::create();
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth);
}
@ -178,10 +180,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g
m_s.transferBalance(_sender, m_newAddress, _endowment);
if (_init.empty())
{
m_s.m_cache[m_newAddress].setCode({});
m_endGas = _gas;
}
return !m_ext;
}
@ -215,17 +214,16 @@ bool Executive::go(OnOpFunc const& _onOp)
#endif
try
{
m_out = m_vm->go(*m_ext, _onOp);
m_endGas = m_vm->gas();
m_out = m_vm->go(m_gas, *m_ext, _onOp);
if (m_isCreation)
{
m_gasForDeposit = m_endGas;
m_gasForDeposit = m_gas;
m_depositSize = m_out.size();
if (m_out.size() * c_createDataGas <= m_endGas)
if (m_out.size() * c_createDataGas <= m_gas)
{
m_codeDeposit = CodeDeposit::Success;
m_endGas -= m_out.size() * c_createDataGas;
m_gas -= m_out.size() * c_createDataGas;
}
else
{
@ -243,7 +241,7 @@ bool Executive::go(OnOpFunc const& _onOp)
catch (VMException const& _e)
{
clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e);
m_endGas = 0;
m_gas = 0;
m_excepted = toTransactionException(_e);
m_ext->revert();
}
@ -273,12 +271,12 @@ void Executive::finalize()
// SSTORE refunds...
// must be done before the miner gets the fees.
if (m_ext)
m_endGas += min((m_t.gas() - m_endGas) / 2, m_ext->sub.refunds);
m_gas += min((m_t.gas() - m_gas) / 2, m_ext->sub.refunds);
// cnote << "Refunding" << formatBalance(m_endGas * m_ext->gasPrice) << "to origin (=" << m_endGas << "*" << formatBalance(m_ext->gasPrice) << ")";
m_s.addBalance(m_t.sender(), m_endGas * m_t.gasPrice());
m_s.addBalance(m_t.sender(), m_gas * m_t.gasPrice());
u256 feesEarned = (m_t.gas() - m_endGas) * m_t.gasPrice();
u256 feesEarned = (m_t.gas() - m_gas) * m_t.gasPrice();
m_s.addBalance(m_s.m_currentBlock.coinbaseAddress, feesEarned);
// Suicides...

8
libethereum/Executive.h

@ -99,14 +99,14 @@ public:
void accrueSubState(SubState& _parentContext);
/// Executes (or continues execution of) the VM.
/// @returns false iff go() must be called again to finish the transction.
/// @returns false iff go() must be called again to finish the transaction.
bool go(OnOpFunc const& _onOp = OnOpFunc());
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc simpleTrace();
/// @returns gas remaining after the transaction/operation.
u256 endGas() const { return m_endGas; }
/// @returns gas remaining after the transaction/operation. Valid after the transaction has been executed.
u256 gas() const { return m_gas; }
/// @returns output data of the transaction/operation.
bytesConstRef out() const { return m_out; }
/// @returns the new address for the created contract in the CREATE operation.
@ -132,7 +132,7 @@ private:
u256 m_gasForDeposit; ///< Amount of gas remaining for the code deposit phase.
CodeDeposit m_codeDeposit = CodeDeposit::None; ///< True if an attempted deposit failed due to lack of gas.
TransactionException m_excepted = TransactionException::None; ///< Details if the VM's execution resulted in an exception.
u256 m_endGas; ///< The final amount of gas for the transaction.
u256 m_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution.
Transaction m_t; ///< The original transaction. Set by setup().
LogEntries m_logs; ///< The log entries created by this transaction. Set by finalize().

4
libethereum/ExtVM.cpp

@ -34,7 +34,7 @@ bool ExtVM::call(Address _receiveAddress, u256 _txValue, bytesConstRef _txData,
e.go(_onOp);
e.accrueSubState(sub);
}
io_gas = e.endGas();
io_gas = e.gas();
e.out().copyTo(_out);
return !e.excepted();
@ -51,7 +51,7 @@ h160 ExtVM::create(u256 _endowment, u256& io_gas, bytesConstRef _code, OnOpFunc
e.go(_onOp);
e.accrueSubState(sub);
}
io_gas = e.endGas();
io_gas = e.gas();
return e.newAddress();
}

23
libevm/VM.cpp

@ -25,13 +25,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 _gas) noexcept
{
VMFace::reset(_gas);
m_curPC = 0;
m_jumpDests.clear();
}
struct InstructionMetric
{
int gasPriceTier;
@ -52,8 +45,14 @@ static array<InstructionMetric, 256> metrics()
return s_ret;
}
bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
// Reset leftovers from possible previous run
m_curPC = 0;
m_jumpDests.clear();
m_gas = io_gas;
m_stack.reserve((unsigned)c_stackLimit);
static const array<InstructionMetric, 256> c_metrics = metrics();
@ -200,11 +199,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
// _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
if (m_gas < runGas)
{
// Out of gas!
m_gas = 0;
BOOST_THROW_EXCEPTION(OutOfGas());
}
m_gas = (u256)((bigint)m_gas - runGas);
@ -655,6 +650,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
io_gas = m_gas;
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
@ -664,9 +660,12 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
// ...follow through to...
}
case Instruction::STOP:
io_gas = m_gas;
return bytesConstRef();
}
}
io_gas = m_gas;
if (_steps == (uint64_t)-1)
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();

9
libevm/VM.h

@ -52,14 +52,13 @@ inline u256 fromAddress(Address _a)
class VM: public VMFace
{
public:
virtual void reset(u256 _gas = 0) noexcept override final;
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void require(u256 _n, u256 _d) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } if (m_stack.size() - _n + _d > c_stackLimit) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(OutOfStack() << RequirementError((bigint)(_d - _n), (bigint)m_stack.size())); } }
void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } }
u256 curPC() const { return m_curPC; }
u256 gas() const { return m_gas; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
@ -67,10 +66,8 @@ public:
private:
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): VMFace(_gas) {}
u256 m_curPC = 0;
u256 m_gas = 0;
bytes m_temp;
u256s m_stack;
std::set<u256> m_jumpDests;

10
libevm/VMFace.h

@ -38,18 +38,12 @@ struct StackUnderflow: virtual VMException {};
class VMFace
{
public:
explicit VMFace(u256 _gas): m_gas(_gas) {}
VMFace() = default;
virtual ~VMFace() = default;
VMFace(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 _gas = 0) noexcept { m_gas = _gas; }
u256 gas() const noexcept { return m_gas; }
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
protected:
u256 m_gas = 0;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
};
}

6
libevm/VMFactory.cpp

@ -37,13 +37,13 @@ void VMFactory::setKind(VMKind _kind)
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
std::unique_ptr<VMFace> VMFactory::create()
{
#if ETH_EVMJIT
return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? static_cast<VMFace*>(new JitVM(_gas)) : static_cast<VMFace*>(new VM(_gas)));
return std::unique_ptr<VMFace>(g_kind == VMKind::JIT ? static_cast<VMFace*>(new JitVM) : static_cast<VMFace*>(new VM));
#else
asserts(g_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
#endif
}

2
libevm/VMFactory.h

@ -34,7 +34,7 @@ class VMFactory
public:
VMFactory() = delete;
static std::unique_ptr<VMFace> create(u256 _gas);
static std::unique_ptr<VMFace> create();
static void setKind(VMKind _kind);
};

8
test/fuzzTesting/checkRandomVMTest.cpp

@ -94,13 +94,11 @@ bool doVMTest(mValue& _v)
}
bytes output;
u256 gas;
bool vmExceptionOccured = false;
try
{
auto vm = eth::VMFactory::create(fev.gas);
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
auto vm = eth::VMFactory::create();
output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes();
}
catch (eth::VMException)
{
@ -168,7 +166,7 @@ bool doVMTest(mValue& _v)
return 1;
}
if (asserts(toInt(o["gas"]) == gas))
if (asserts(toInt(o["gas"]) == fev.gas))
return 1;
auto& expectedAddrs = test.addresses;

8
test/fuzzTesting/createRandomVMTest.cpp

@ -155,14 +155,12 @@ void doMyTests(json_spirit::mValue& _v)
}
bytes output;
auto vm = eth::VMFactory::create(fev.gas);
auto vm = eth::VMFactory::create();
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm->go(fev, fev.simpleTrace()).toBytes();
gas = vm->gas();
output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes();
}
catch (eth::VMException const& _e)
{
@ -201,7 +199,7 @@ void doMyTests(json_spirit::mValue& _v)
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = toHex(output, 2, HexPrefix::Add);
o["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1);
o["logs"] = test::exportLog(fev.sub.logs);
}
}

10
test/libevm/vm.cpp

@ -322,19 +322,17 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
}
bytes output;
u256 gas;
bool vmExceptionOccured = false;
try
{
auto vm = eth::VMFactory::create(fev.gas);
auto vm = eth::VMFactory::create();
auto vmtrace = Options::get().vmtrace ? fev.simpleTrace() : OnOpFunc{};
auto outputRef = bytesConstRef{};
{
Listener::ExecTimeGuard guard{i.first};
outputRef = vm->go(fev, vmtrace);
outputRef = vm->go(fev.gas, fev, vmtrace);
}
output = outputRef.toBytes();
gas = vm->gas();
}
catch (VMException const&)
{
@ -389,7 +387,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates();
o["out"] = toHex(output, 2, HexPrefix::Add);
o["gas"] = toCompactHex(gas, HexPrefix::Add, 1);
o["gas"] = toCompactHex(fev.gas, HexPrefix::Add, 1);
o["logs"] = exportLog(fev.sub.logs);
}
}
@ -412,7 +410,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkOutput(output, o);
BOOST_CHECK_EQUAL(toInt(o["gas"]), gas);
BOOST_CHECK_EQUAL(toInt(o["gas"]), fev.gas);
State postState, expectState;
mObject mPostState = fev.exportState();

Loading…
Cancel
Save