Browse Source

Merge pull request #1860 from imapp-pl/feature/vm_gas_counter_refactor

Remove gas counter from VM interface
cl-refactor
Gav Wood 10 years ago
parent
commit
c48523bb86
  1. 4
      alethzero/Debugger.cpp
  2. 12
      eth/main.cpp
  3. 16
      evmjit/libevmjit-cpp/JitVM.cpp
  4. 10
      evmjit/libevmjit-cpp/JitVM.h
  5. 2
      libethereum/Client.cpp
  6. 89
      libethereum/Executive.cpp
  7. 10
      libethereum/Executive.h
  8. 4
      libethereum/ExtVM.cpp
  9. 2
      libevm/ExtVMFace.h
  10. 10
      libevm/SmartVM.cpp
  11. 8
      libevm/SmartVM.h
  12. 37
      libevm/VM.cpp
  13. 15
      libevm/VM.h
  14. 5
      libevm/VMFace.h
  15. 14
      libevm/VMFactory.cpp
  16. 4
      libevm/VMFactory.h
  17. 18
      mix/MixClient.cpp
  18. 8
      test/fuzzTesting/checkRandomVMTest.cpp
  19. 8
      test/fuzzTesting/createRandomVMTest.cpp
  20. 18
      test/libevm/vm.cpp
  21. 2
      test/libsolidity/solidityExecutionFramework.h

4
alethzero/Debugger.cpp

@ -82,7 +82,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
bytesConstRef lastData;
h256 lastHash;
h256 lastDataHash;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
VM& vm = *voidVM;
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -104,7 +104,7 @@ bool DebugSession::populate(dev::eth::Executive& _executive, dev::eth::Transacti
levels.push_back(&history.back());
else
levels.resize(ext.depth);
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, vm.gas(), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
history.append(WorldState({steps, ext.myAddress, vm.curPC(), inst, newMemSize, static_cast<u256>(gas), lastHash, lastDataHash, vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels}));
};
_executive.go(onOp);
_executive.finalize();

12
eth/main.cpp

@ -1187,7 +1187,7 @@ int main(int argc, char** argv)
{
OnOpFunc oof;
if (format == "pretty")
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t steps, Instruction instr, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
@ -1198,24 +1198,24 @@ int main(int argc, char** argv)
f << " STORAGE" << endl;
for (auto const& i: ext->state().storage(ext->myAddress))
f << showbase << hex << i.first << ": " << i.second << endl;
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << vm->gas() << " | -" << dec << gasCost << " | " << newMemSize << "x32";
f << dec << ext->depth << " | " << ext->myAddress << " | #" << steps << " | " << hex << setw(4) << setfill('0') << vm->curPC() << " : " << dev::eth::instructionInfo(instr).name << " | " << dec << gas << " | -" << dec << gasCost << " | " << newMemSize << "x32";
};
else if (format == "standard")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
else if (format == "standard+")
oof = [&](uint64_t, Instruction instr, bigint, bigint, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
oof = [&](uint64_t, Instruction instr, bigint, bigint, bigint gas, dev::eth::VM* vvm, dev::eth::ExtVMFace const* vextVM)
{
dev::eth::VM* vm = vvm;
dev::eth::ExtVM const* ext = static_cast<ExtVM const*>(vextVM);
if (instr == Instruction::STOP || instr == Instruction::RETURN || instr == Instruction::SUICIDE)
for (auto const& i: ext->state().storage(ext->myAddress))
f << toHex(dev::toCompactBigEndian(i.first, 1)) << " " << toHex(dev::toCompactBigEndian(i.second, 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)vm->gas(), 1)) << endl;
f << ext->myAddress << " " << hex << toHex(dev::toCompactBigEndian(vm->curPC(), 1)) << " " << hex << toHex(dev::toCompactBigEndian((int)(byte)instr, 1)) << " " << hex << toHex(dev::toCompactBigEndian((uint64_t)gas, 1)) << endl;
};
e.initialize(t);
if (!e.execute())

16
evmjit/libevmjit-cpp/JitVM.cpp

@ -18,27 +18,25 @@ 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";
m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
m_fallbackVM = VMFactory::create(VMKind::Interpreter);
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();
@ -78,7 +76,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)};
}

10
evmjit/libevmjit-cpp/JitVM.h

@ -10,16 +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 u256 gas() const noexcept { return m_gas; }
virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; }
public:
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): m_gas(_gas) {}
u256 m_gas;
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

2
libethereum/Client.cpp

@ -423,7 +423,7 @@ ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256
temp = m_postMine;
temp.addBalance(_from, _value + _gasPrice * _gas);
Executive e(temp, LastHashes(), 0);
if (!e.call(_dest, _dest, _from, _value, _gasPrice, &_data, _gas, _from))
if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas))
e.go();
ret = e.executionResult();
}

89
libethereum/Executive.cpp

@ -42,7 +42,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
@ -121,75 +121,45 @@ bool Executive::execute()
if (m_t.isCreation())
return create(m_t.sender(), m_t.value(), m_t.gasPrice(), m_t.gas() - (u256)m_t.gasRequired(), &m_t.data(), m_t.sender());
else
return call(m_t.receiveAddress(), m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired(), m_t.sender());
return call(m_t.receiveAddress(), m_t.sender(), m_t.value(), m_t.gasPrice(), bytesConstRef(&m_t.data()), m_t.gas() - (u256)m_t.gasRequired());
}
bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas, Address _originAddress)
bool Executive::call(Address _receiveAddress, Address _senderAddress, u256 _value, u256 _gasPrice, bytesConstRef _data, u256 _gas)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_codeAddress) : precompiled().end();
if (it != precompiled().end())
{
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_precompiledOut = it->second.exec(_data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_codeAddress))
{
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);
}
else
m_endGas = _gas;
m_s.transferBalance(_senderAddress, _receiveAddress, _value);
return !m_ext;
CallParameters params{_senderAddress, _receiveAddress, _receiveAddress, _gas, _value, _data, {}, {}};
return call(params, _gasPrice, _senderAddress);
}
bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address const& _origin)
{
m_isCreation = false;
// cnote << "Transferring" << formatBalance(_value) << "to receiver.";
auto it = !(_p.codeAddress & ~h160(0xffffffff)) ? precompiled().find((unsigned)(u160)_p.codeAddress) : precompiled().end();
if (it != precompiled().end())
{
bigint g = it->second.gas(_p.data);
if (_p.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)(_p.gas - g);
m_gas = (u256)(_p.gas - g);
m_precompiledOut = it->second.exec(_p.data);
m_out = &m_precompiledOut;
}
}
else if (m_s.addressHasCode(_p.codeAddress))
else
{
m_vm = VMFactory::create(_p.gas);
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
m_gas = _p.gas;
if (m_s.addressHasCode(_p.codeAddress))
{
m_vm = VMFactory::create();
bytes const& c = m_s.code(_p.codeAddress);
m_ext = make_shared<ExtVM>(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth);
}
}
else
m_endGas = _p.gas;
m_s.transferBalance(_p.senderAddress, _p.receiveAddress, _p.value);
@ -203,11 +173,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);
}
@ -215,17 +186,14 @@ 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;
}
OnOpFunc Executive::simpleTrace()
{
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
return [](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
@ -239,13 +207,13 @@ OnOpFunc Executive::simpleTrace()
for (auto const& i: ext.state().storage(ext.myAddress))
o << showbase << hex << i.first << ": " << i.second << endl;
dev::LogOutputStream<VMTraceChannel, false>() << o.str();
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
dev::LogOutputStream<VMTraceChannel, false>() << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
OnOpFunc Executive::standardTrace(ostream& o_output)
{
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, VM* voidVM, ExtVMFace const* voidExt)
return [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, VM* voidVM, ExtVMFace const* voidExt)
{
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
VM& vm = *voidVM;
@ -257,7 +225,7 @@ OnOpFunc Executive::standardTrace(ostream& o_output)
o_output << " STORAGE" << endl;
for (auto const& i: ext.state().storage(ext.myAddress))
o_output << showbase << hex << i.first << ": " << i.second << endl;
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << vm.gas() << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
o_output << " < " << dec << ext.depth << " : " << ext.myAddress << " : #" << steps << " : " << hex << setw(4) << setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " : " << dec << gas << " : -" << dec << gasCost << " : " << newMemSize << "x32" << " >";
};
}
@ -270,17 +238,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
{
@ -298,7 +265,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();
@ -331,12 +298,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...

10
libethereum/Executive.h

@ -95,13 +95,13 @@ public:
bool create(Address _txSender, u256 _endowment, u256 _gasPrice, u256 _gas, bytesConstRef _code, Address _originAddress);
/// Set up the executive for evaluating a bare CALL (message call) operation.
/// @returns false iff go() must be called (and thus a VM execution in required).
bool call(Address _myAddress, Address _codeAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas, Address _originAddress);
bool call(Address _receiveAddress, Address _txSender, u256 _txValue, u256 _gasPrice, bytesConstRef _txData, u256 _gas);
bool call(CallParameters const& _cp, u256 const& _gasPrice, Address const& _origin);
/// Finalise an operation through accruing the substate into the parent context.
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.
@ -110,8 +110,8 @@ public:
/// Operation function for providing a simple trace of the VM execution.
static OnOpFunc standardTrace(std::ostream& o_output);
/// @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.
@ -137,7 +137,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(CallParameters& _p)
e.go(_p.onOp);
e.accrueSubState(sub);
}
_p.gas = e.endGas();
_p.gas = e.gas();
e.out().copyTo(_p.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();
}

2
libevm/ExtVMFace.h

@ -106,7 +106,7 @@ class VM;
using LastHashes = std::vector<h256>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, VM*, ExtVMFace const*)>;
using OnOpFunc = std::function<void(uint64_t /*steps*/, Instruction /*instr*/, bigint /*newMemSize*/, bigint /*gasCost*/, bigint /*gas*/, VM*, ExtVMFace const*)>;
struct CallParameters
{

10
libevm/SmartVM.cpp

@ -41,7 +41,7 @@ namespace
}
}
bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
bytesConstRef SmartVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
{
auto codeHash = sha3(_ext.code);
auto vmKind = VMKind::Interpreter; // default VM
@ -66,11 +66,9 @@ bytesConstRef SmartVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step
}
// TODO: Selected VM must be kept only because it returns reference to its internal memory.
// VM implementations should be stateless, without gas counter and escaping memory reference.
m_selectedVM = VMFactory::create(vmKind, gas());
auto out = m_selectedVM->go(_ext, _onOp, _steps);
m_gas = m_selectedVM->gas();
return out;
// VM implementations should be stateless, without escaping memory reference.
m_selectedVM = VMFactory::create(vmKind);
return m_selectedVM->go(io_gas, _ext, _onOp, _steps);
}
}

8
libevm/SmartVM.h

@ -31,16 +31,10 @@ namespace eth
class SmartVM: public VMFace
{
public:
SmartVM(u256 const& _gas): m_gas(_gas) {}
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
void reset(u256 const& _gas = 0) noexcept override { m_gas = _gas; }
u256 gas() const noexcept override { return (u256)m_gas; }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
private:
std::unique_ptr<VMFace> m_selectedVM;
bigint m_gas;
};
}

37
libevm/VM.cpp

@ -25,13 +25,6 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
void VM::reset(u256 const& _gas) noexcept
{
m_gas = _gas;
m_curPC = 0;
m_jumpDests.clear();
}
struct InstructionMetric
{
int gasPriceTier;
@ -52,8 +45,12 @@ 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_stack.reserve((unsigned)c_stackLimit);
unique_ptr<CallParameters> callParams;
@ -100,7 +97,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
auto onOperation = [&]()
{
if (_onOp)
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, this, &_ext);
_onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext);
};
switch (inst)
@ -198,17 +195,11 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
runGas += c_copyGas * ((copySize + 31) / 32);
onOperation();
// if (_onOp)
// _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;
if (io_gas < runGas)
BOOST_THROW_EXCEPTION(OutOfGas());
}
m_gas -= runGas;
io_gas -= (u256)runGas;
if (newTempSize > m_temp.size())
m_temp.resize((size_t)newTempSize);
@ -567,7 +558,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.push_back(m_temp.size());
break;
case Instruction::GAS:
m_stack.push_back((u256)m_gas);
m_stack.push_back(io_gas);
break;
case Instruction::JUMPDEST:
break;
@ -616,11 +607,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
if (_ext.balance(_ext.myAddress) >= endowment && _ext.depth < 1024)
{
u256 g(m_gas);
m_stack.push_back((u160)_ext.create(endowment, g, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
m_gas = g;
}
m_stack.push_back((u160)_ext.create(endowment, io_gas, bytesConstRef(m_temp.data() + initOff, initSize), _onOp));
else
m_stack.push_back(0);
break;
@ -661,7 +648,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
else
m_stack.push_back(0);
m_gas += callParams->gas;
io_gas += callParams->gas;
break;
}
case Instruction::RETURN:
@ -670,7 +657,6 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
m_stack.pop_back();
unsigned s = (unsigned)m_stack.back();
m_stack.pop_back();
return bytesConstRef(m_temp.data() + b, s);
}
case Instruction::SUICIDE:
@ -683,6 +669,7 @@ bytesConstRef VM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps)
return bytesConstRef();
}
}
if (_steps == (uint64_t)-1)
BOOST_THROW_EXCEPTION(StepsDone());
return bytesConstRef();

15
libevm/VM.h

@ -52,31 +52,22 @@ inline u256 fromAddress(Address _a)
class VM: public VMFace
{
public:
virtual bytesConstRef go(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); } }
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
u256 curPC() const { return m_curPC; }
bytes const& memory() const { return m_temp; }
u256s const& stack() const { return m_stack; }
void reset(u256 const& _gas = 0) noexcept override;
u256 gas() const noexcept override { return (u256)m_gas; }
private:
friend class VMFactory;
/// Construct VM object.
explicit VM(u256 _gas): m_gas(_gas) {}
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 m_curPC = 0;
bytes m_temp;
u256s m_stack;
std::vector<uint64_t> m_jumpDests;
std::function<void()> m_onFail;
bigint m_gas = 0;
};
}

5
libevm/VMFace.h

@ -43,10 +43,7 @@ public:
VMFace(VMFace const&) = delete;
VMFace& operator=(VMFace const&) = delete;
virtual void reset(u256 const& _gas = 0) noexcept = 0;
virtual u256 gas() const noexcept = 0;
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0;
};
}

14
libevm/VMFactory.cpp

@ -38,27 +38,27 @@ void VMFactory::setKind(VMKind _kind)
g_kind = _kind;
}
std::unique_ptr<VMFace> VMFactory::create(u256 _gas)
std::unique_ptr<VMFace> VMFactory::create()
{
return create(g_kind, _gas);
return create(g_kind);
}
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind, u256 _gas)
std::unique_ptr<VMFace> VMFactory::create(VMKind _kind)
{
#if ETH_EVMJIT
switch (_kind)
{
default:
case VMKind::Interpreter:
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
case VMKind::JIT:
return std::unique_ptr<VMFace>(new JitVM(_gas));
return std::unique_ptr<VMFace>(new JitVM);
case VMKind::Smart:
return std::unique_ptr<VMFace>(new SmartVM(_gas));
return std::unique_ptr<VMFace>(new SmartVM);
}
#else
asserts(_kind == VMKind::Interpreter && "JIT disabled in build configuration");
return std::unique_ptr<VMFace>(new VM(_gas));
return std::unique_ptr<VMFace>(new VM);
#endif
}

4
libevm/VMFactory.h

@ -36,10 +36,10 @@ public:
VMFactory() = delete;
/// Creates a VM instance of global kind (controlled by setKind() function).
static std::unique_ptr<VMFace> create(u256 _gas);
static std::unique_ptr<VMFace> create();
/// Creates a VM instance of kind provided.
static std::unique_ptr<VMFace> create(VMKind _kind, u256 _gas);
static std::unique_ptr<VMFace> create(VMKind _kind);
/// Set global VM kind
static void setKind(VMKind _kind);

18
mix/MixClient.cpp

@ -143,7 +143,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
bytesConstRef const* lastData = nullptr;
unsigned codeIndex = 0;
unsigned dataIndex = 0;
auto onOp = [&](uint64_t steps, Instruction inst, dev::bigint newMemSize, dev::bigint gasCost, void* voidVM, void const* voidExt)
auto onOp = [&](uint64_t steps, Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, void* voidVM, void const* voidExt)
{
VM& vm = *static_cast<VM*>(voidVM);
ExtVM const& ext = *static_cast<ExtVM const*>(voidExt);
@ -180,8 +180,20 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c
else
levels.resize(ext.depth);
machineStates.emplace_back(MachineState({steps, vm.curPC(), inst, newMemSize, vm.gas(),
vm.stack(), vm.memory(), gasCost, ext.state().storage(ext.myAddress), levels, codeIndex, dataIndex}));
machineStates.push_back(MachineState{
steps,
vm.curPC(),
inst,
newMemSize,
static_cast<u256>(gas),
vm.stack(),
vm.memory(),
gasCost,
ext.state().storage(ext.myAddress),
std::move(levels),
codeIndex,
dataIndex
});
};
execution.go(onOp);

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);
}
}

18
test/libevm/vm.cpp

@ -231,7 +231,7 @@ void FakeExtVM::importCallCreates(mArray& _callcreates)
eth::OnOpFunc FakeExtVM::simpleTrace()
{
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
return [](uint64_t steps, eth::Instruction inst, bigint newMemSize, bigint gasCost, bigint gas, dev::eth::VM* voidVM, dev::eth::ExtVMFace const* voidExt)
{
FakeExtVM const& ext = *static_cast<FakeExtVM const*>(voidExt);
eth::VM& vm = *voidVM;
@ -247,7 +247,7 @@ eth::OnOpFunc FakeExtVM::simpleTrace()
o << std::showbase << std::hex << i.first << ": " << i.second << std::endl;
dev::LogOutputStream<eth::VMTraceChannel, false>() << o.str();
dev::LogOutputStream<eth::VMTraceChannel, false>() << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << vm.gas() << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
dev::LogOutputStream<eth::VMTraceChannel, false>() << " | " << std::dec << ext.depth << " | " << ext.myAddress << " | #" << steps << " | " << std::hex << std::setw(4) << std::setfill('0') << vm.curPC() << " : " << instructionInfo(inst).name << " | " << std::dec << gas << " | -" << std::dec << gasCost << " | " << newMemSize << "x32" << " ]";
/*creates json stack trace*/
if (eth::VMTraceChannel::verbosity <= g_logVerbosity)
@ -276,7 +276,7 @@ eth::OnOpFunc FakeExtVM::simpleTrace()
/*add all the other details*/
o_step.push_back(Pair("storage", storage));
o_step.push_back(Pair("depth", to_string(ext.depth)));
o_step.push_back(Pair("gas", (string)vm.gas()));
o_step.push_back(Pair("gas", (string)gas));
o_step.push_back(Pair("address", toString(ext.myAddress )));
o_step.push_back(Pair("step", steps ));
o_step.push_back(Pair("pc", (int)vm.curPC()));
@ -324,19 +324,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&)
{
@ -391,7 +389,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
o["callcreates"] = fev.exportCallCreates();
o["out"] = output.size() > 4096 ? "#" + toString(output.size()) : 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);
}
}
@ -414,7 +412,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();
@ -424,7 +422,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
checkCallCreates(fev.callcreates, test.callcreates);
checkCallCreates(test.callcreates, fev.callcreates);
checkLog(fev.sub.logs, test.sub.logs);
}

2
test/libsolidity/solidityExecutionFramework.h

@ -170,7 +170,7 @@ protected:
else
{
BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas, m_sender));
BOOST_REQUIRE(!executive.call(m_contractAddress, m_sender, _value, m_gasPrice, &_data, m_gas));
}
BOOST_REQUIRE(executive.go());
m_state.noteSending(m_sender);

Loading…
Cancel
Save