diff --git a/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h index 7c8b4ceb8..03c3c8880 100644 --- a/evmjit/libevmjit-cpp/JitVM.h +++ b/evmjit/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; private: jit::RuntimeData m_data; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 6dec81b38..d9813c725 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -440,9 +440,10 @@ 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); + e.setResultRef(ret); if (!e.call(_dest, _from, _value, _gasPrice, &_data, _gas)) e.go(); - ret = e.executionResult(); + e.finalize(); } catch (...) { diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 122432d85..fc8ba22a3 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -44,11 +44,6 @@ u256 Executive::gasUsed() const return m_t.gas() - m_gas; } -ExecutionResult Executive::executionResult() const -{ - return ExecutionResult(gasUsed(), m_excepted, m_newAddress, out(), m_codeDeposit, m_ext ? m_ext->sub.refunds : 0, m_depositSize, m_gasForDeposit); -} - void Executive::accrueSubState(SubState& _parentContext) { if (m_ext) @@ -145,7 +140,8 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co else { m_gas = (u256)(_p.gas - g); - m_out = it->second.exec(_p.data); + auto out = it->second.exec(_p.data); // FIXME: Avoid double copy + bytesConstRef{&out}.copyTo(_p.out); } } else @@ -153,7 +149,7 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co m_gas = _p.gas; if (m_s.addressHasCode(_p.codeAddress)) { - m_vm = VMFactory::create(); + m_outRef = _p.out; // Save ref to expected output buffer to be used in go() bytes const& c = m_s.code(_p.codeAddress); m_ext = make_shared(m_s, m_lastHashes, _p.receiveAddress, _p.senderAddress, _origin, _p.value, _gasPrice, _p.data, &c, m_depth); } @@ -175,10 +171,7 @@ bool Executive::create(Address _sender, u256 _endowment, u256 _gasPrice, u256 _g // Execute _init. if (!_init.empty()) - { - m_vm = VMFactory::create(); m_ext = make_shared(m_s, m_lastHashes, m_newAddress, _sender, _origin, _endowment, _gasPrice, bytesConstRef(), _init, m_depth); - } m_s.m_cache[m_newAddress] = Account(m_s.balance(m_newAddress), Account::ContractConception); m_s.transferBalance(_sender, m_newAddress, _endowment); @@ -211,30 +204,47 @@ OnOpFunc Executive::simpleTrace() bool Executive::go(OnOpFunc const& _onOp) { - if (m_vm) + if (m_ext) { #if ETH_TIMED_EXECUTIONS boost::timer t; #endif try { - m_out = m_vm->exec(m_gas, *m_ext, _onOp); - + auto vm = VMFactory::create(); if (m_isCreation) { - m_gasForDeposit = m_gas; - m_depositSize = m_out.size(); - if (m_out.size() * c_createDataGas <= m_gas) + auto out = vm->exec(m_gas, *m_ext, _onOp); + if (m_res) + { + m_res->gasForDeposit = m_gas; + m_res->depositSize = out.size(); + } + if (out.size() * c_createDataGas <= m_gas) { - m_codeDeposit = CodeDeposit::Success; - m_gas -= m_out.size() * c_createDataGas; + if (m_res) + m_res->codeDeposit = CodeDeposit::Success; + m_gas -= out.size() * c_createDataGas; } else { - m_codeDeposit = CodeDeposit::Failed; - m_out.clear(); + if (m_res) + m_res->codeDeposit = CodeDeposit::Failed; + out.clear(); } - m_s.m_cache[m_newAddress].setCode(std::move(m_out)); + if (m_res) + m_res->output = out; // copy output to execution result + m_s.m_cache[m_newAddress].setCode(std::move(out)); // FIXME: Set only if Success? + } + else + { + if (m_res) + { + m_res->output = vm->exec(m_gas, *m_ext, _onOp); // take full output + bytesConstRef{&m_res->output}.copyTo(m_outRef); + } + else + vm->exec(m_gas, *m_ext, m_outRef, _onOp); // take only expected output } } catch (StepsDone const&) @@ -293,4 +303,12 @@ void Executive::finalize() // Logs.. if (m_ext) m_logs = m_ext->sub.logs; + + if (m_res) // Collect results + { + m_res->gasUsed = gasUsed(); + m_res->excepted = m_excepted; // TODO: m_except is used only in ExtVM::call + m_res->newAddress = m_newAddress; + m_res->gasRefunded = m_ext ? m_ext->sub.refunds : 0; + } } diff --git a/libethereum/Executive.h b/libethereum/Executive.h index bd9fb178e..8b2906b9b 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -108,29 +108,25 @@ public: /// @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.data(), m_out.size()}; } + /// @returns the new address for the created contract in the CREATE operation. h160 newAddress() const { return m_newAddress; } /// @returns true iff the operation ended with a VM exception. bool excepted() const { return m_excepted != TransactionException::None; } - /// Get the above in an amalgamated fashion. - ExecutionResult executionResult() const; + /// Collect execution results in the result storage provided. + void setResultRef(ExecutionResult& _res) { m_res = &_res; } private: State& m_s; ///< The state to which this operation/transaction is applied. LastHashes m_lastHashes; std::shared_ptr m_ext; ///< The VM externality object for the VM execution or null if no VM is required. - std::unique_ptr m_vm; ///< The VM object or null if no VM is required. - bytes m_out; ///< The VM execution output. + bytesRef m_outRef; ///< Reference to "expected output" buffer. + ExecutionResult* m_res = nullptr; ///< Optional storage for execution results. Address m_newAddress; ///< The address of the created contract in the case of create() being called. unsigned m_depth = 0; ///< The context's call-depth. bool m_isCreation = false; ///< True if the transaction creates a contract, or if create() is called. - unsigned m_depositSize = 0; ///< Amount of code of the creation's attempted deposit. - 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_gas = 0; ///< The gas for EVM code execution. Initial amount before go() execution, final amount after go() execution. diff --git a/libethereum/ExtVM.cpp b/libethereum/ExtVM.cpp index 488d8e4b7..782d4d79c 100644 --- a/libethereum/ExtVM.cpp +++ b/libethereum/ExtVM.cpp @@ -35,7 +35,6 @@ bool ExtVM::call(CallParameters& _p) e.accrueSubState(sub); } _p.gas = e.gas(); - e.out().copyTo(_p.out); return !e.excepted(); } diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c753f57ea..277e9a649 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1125,6 +1125,8 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per // Create and initialize the executive. This will throw fairly cheaply and quickly if the // transaction is bad in any way. Executive e(*this, _lh, 0); + ExecutionResult res; + e.setResultRef(res); e.initialize(_t); // Uncommitting is a non-trivial operation - only do it once we've verified as much of the @@ -1181,7 +1183,7 @@ ExecutionResult State::execute(LastHashes const& _lh, Transaction const& _t, Per m_transactionSet.insert(e.t().sha3()); } - return e.executionResult(); + return res; } State State::fromPending(unsigned _i) const diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 09d6cd54c..3a78ced26 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -74,25 +74,14 @@ TransactionException toTransactionException(VMException const& _e); /// Description of the result of executing a transaction. struct ExecutionResult { - ExecutionResult() = default; - ExecutionResult(u256 const& _gasUsed, TransactionException _excepted, Address const& _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit, u256 const& _gasRefund, unsigned _depositSize, u256 const& _gasForDeposit): - gasUsed(_gasUsed), - excepted(_excepted), - newAddress(_newAddress), - output(_output.toBytes()), - codeDeposit(_codeDeposit), - gasRefunded(_gasRefund), - depositSize(_depositSize), - gasForDeposit(_gasForDeposit) - {} u256 gasUsed = 0; TransactionException excepted = TransactionException::Unknown; Address newAddress; bytes output; - CodeDeposit codeDeposit = CodeDeposit::None; + CodeDeposit codeDeposit = CodeDeposit::None; ///< Failed if an attempted deposit failed due to lack of gas. u256 gasRefunded = 0; - unsigned depositSize = 0; - u256 gasForDeposit; + unsigned depositSize = 0; ///< Amount of code of the creation's attempted deposit. + u256 gasForDeposit; ///< Amount of gas remaining for the code deposit phase. }; std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); diff --git a/libevm/SmartVM.h b/libevm/SmartVM.h index 01e163cd2..73d515f29 100644 --- a/libevm/SmartVM.h +++ b/libevm/SmartVM.h @@ -31,7 +31,7 @@ namespace eth class SmartVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; private: std::unique_ptr m_selectedVM; diff --git a/libevm/VM.h b/libevm/VM.h index 0a33e9fae..da3a843ed 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -52,7 +52,7 @@ inline u256 fromAddress(Address _a) class VM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; u256 curPC() const { return m_curPC; } diff --git a/libevm/VMFace.h b/libevm/VMFace.h index 8fd7ebcb3..7a344a072 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -43,12 +43,22 @@ public: VMFace(VMFace const&) = delete; VMFace& operator=(VMFace const&) = delete; + /// Execute EVM code by VM. + /// + /// @param _out Expected output + void exec(u256& io_gas, ExtVMFace& _ext, bytesRef _out, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) + { + execImpl(io_gas, _ext, _onOp, _steps).copyTo(_out); + } + + /// The same as above but returns a copy of full output. bytes exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) { return execImpl(io_gas, _ext, _onOp, _steps).toVector(); } - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; + /// VM implementation + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) = 0; }; } diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index b011e0cec..2644a8c65 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -130,7 +130,9 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c State execState = _state; execState.addBalance(t.sender(), t.gas() * t.gasPrice()); //give it enough balance for gas estimation + eth::ExecutionResult er; Executive execution(execState, lastHashes, 0); + execution.setResultRef(er); execution.initialize(t); execution.execute(); std::vector machineStates; @@ -186,7 +188,6 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c execution.go(onOp); execution.finalize(); - dev::eth::ExecutionResult er = execution.executionResult(); switch (er.excepted) { @@ -213,7 +214,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c }; ExecutionResult d; - d.result = execution.executionResult(); + d.result = er; d.machineStates = machineStates; d.executionCode = std::move(codes); d.transactionData = std::move(data); diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index 0f80e9f59..ea277421a 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -135,6 +135,8 @@ protected: { m_state.addBalance(m_sender, _value); // just in case eth::Executive executive(m_state, eth::LastHashes(), 0); + eth::ExecutionResult res; + executive.setResultRef(res); eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); bytes transactionRLP = t.rlp(); @@ -161,7 +163,7 @@ protected: m_state.noteSending(m_sender); executive.finalize(); m_gasUsed = executive.gasUsed(); - m_output = executive.out().toVector(); + m_output = std::move(res.output); // FIXME: Looks like Framework needs ExecutiveResult embedded m_logs = executive.logs(); }