diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 5193168a4..0d6a6e00a 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -18,7 +18,7 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { using namespace jit; @@ -33,7 +33,7 @@ bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, ui { 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); + return m_fallbackVM->execImpl(io_gas, _ext, _onOp); } m_data.gas = static_cast(io_gas); diff --git a/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h index e6864f885..e97abd83b 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 go(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) override final; private: jit::RuntimeData m_data; diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 814f8309e..e68381427 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -37,7 +37,7 @@ using namespace dev::crypto; static Secp256k1 s_secp256k1; -bool dev::SignatureStruct::isValid() const +bool dev::SignatureStruct::isValid() const noexcept { if (v > 1 || r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") || diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 10bcdd067..7bb51e563 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -51,7 +51,7 @@ struct SignatureStruct operator Signature() const { return *(h520 const*)this; } /// @returns true if r,s,v values are valid, otherwise false - bool isValid() const; + bool isValid() const noexcept; h256 r; h256 s; diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 46fbbdfb1..9e2dfc0a6 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -429,9 +429,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.setResultRecipient(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 c2830729d..5ec5f7313 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -45,11 +45,6 @@ u256 Executive::gasUsed() const return m_t.gas() - m_gas; } -ExecutionResult Executive::executionResult() const -{ - return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit, m_ext ? m_ext->sub.refunds : 0, m_depositSize, m_gasForDeposit); -} - void Executive::accrueSubState(SubState& _parentContext) { if (m_ext) @@ -146,8 +141,7 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co else { m_gas = (u256)(_p.gas - g); - m_precompiledOut = it->second.exec(_p.data); - m_out = &m_precompiledOut; + it->second.exec(_p.data, _p.out); } } else @@ -155,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); } @@ -177,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); @@ -231,36 +222,48 @@ OnOpFunc Executive::standardTrace(ostream& o_output) 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->go(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_codeDeposit = CodeDeposit::Success; - m_gas -= m_out.size() * c_createDataGas; + m_res->gasForDeposit = m_gas; + m_res->depositSize = out.size(); + } + if (out.size() * c_createDataGas <= m_gas) + { + if (m_res) + m_res->codeDeposit = CodeDeposit::Success; + m_gas -= out.size() * c_createDataGas; } else { - - m_codeDeposit = CodeDeposit::Failed; - m_out.reset(); + if (m_res) + m_res->codeDeposit = CodeDeposit::Failed; + out.clear(); } - m_s.m_cache[m_newAddress].setCode(m_out.toBytes()); + 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&) - { - return false; } catch (VMException const& _e) { @@ -314,4 +317,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 d5d1baf87..beeee3331 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -112,30 +112,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; } + /// @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 setResultRecipient(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_precompiledOut; ///< Used for the output when there is no VM for a contract (i.e. precompiled). - bytesConstRef m_out; ///< The copyable 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/Precompiled.cpp b/libethereum/Precompiled.cpp index 0e80949fe..0ae9bf3eb 100644 --- a/libethereum/Precompiled.cpp +++ b/libethereum/Precompiled.cpp @@ -31,7 +31,10 @@ using namespace std; using namespace dev; using namespace dev::eth; -static bytes ecrecoverCode(bytesConstRef _in) +namespace +{ + +void ecrecoverCode(bytesConstRef _in, bytesRef _out) { struct inType { @@ -44,47 +47,49 @@ static bytes ecrecoverCode(bytesConstRef _in) memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); h256 ret; - - if ((u256)in.v > 28) - return ret.asBytes(); - SignatureStruct sig(in.r, in.s, (byte)((int)(u256)in.v - 27)); - if (!sig.isValid()) - return ret.asBytes(); - - try + u256 v = (u256)in.v; + if (v >= 27 && v <= 28) { - ret = dev::sha3(recover(sig, in.hash)); + SignatureStruct sig(in.r, in.s, (byte)((int)v - 27)); + if (sig.isValid()) + { + try + { + ret = sha3(recover(sig, in.hash)); + } + catch (...) {} + } } - catch (...) {} memset(ret.data(), 0, 12); - return ret.asBytes(); + ret.ref().copyTo(_out); } -static bytes sha256Code(bytesConstRef _in) +void sha256Code(bytesConstRef _in, bytesRef _out) { - return sha256(_in).asBytes(); + sha256(_in).ref().copyTo(_out); } -static bytes ripemd160Code(bytesConstRef _in) +void ripemd160Code(bytesConstRef _in, bytesRef _out) { - return h256(ripemd160(_in), h256::AlignRight).asBytes(); + h256(ripemd160(_in), h256::AlignRight).ref().copyTo(_out); } -static bytes identityCode(bytesConstRef _in) +void identityCode(bytesConstRef _in, bytesRef _out) { - return _in.toBytes(); + _in.copyTo(_out); } -static const std::unordered_map c_precompiled = -{ - { 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }}, - { 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }}, - { 3, { [](bytesConstRef i) -> bigint { return c_ripemd160Gas + (i.size() + 31) / 32 * c_ripemd160WordGas; }, ripemd160Code }}, - { 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }} -}; +} std::unordered_map const& dev::eth::precompiled() { + static const std::unordered_map c_precompiled = + { + { 1, { [](bytesConstRef) -> bigint { return c_ecrecoverGas; }, ecrecoverCode }}, + { 2, { [](bytesConstRef i) -> bigint { return c_sha256Gas + (i.size() + 31) / 32 * c_sha256WordGas; }, sha256Code }}, + { 3, { [](bytesConstRef i) -> bigint { return c_ripemd160Gas + (i.size() + 31) / 32 * c_ripemd160WordGas; }, ripemd160Code }}, + { 4, { [](bytesConstRef i) -> bigint { return c_identityGas + (i.size() + 31) / 32 * c_identityWordGas; }, identityCode }} + }; return c_precompiled; } diff --git a/libethereum/Precompiled.h b/libethereum/Precompiled.h index bded27386..b50e51ecd 100644 --- a/libethereum/Precompiled.h +++ b/libethereum/Precompiled.h @@ -34,7 +34,7 @@ namespace eth struct PrecompiledAddress { std::function gas; - std::function exec; + std::function exec; }; /// Info on precompiled contract accounts baked into the protocol. diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 92c84c9b3..f40574fde 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1171,6 +1171,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.setResultRecipient(res); e.initialize(_t); // Uncommitting is a non-trivial operation - only do it once we've verified as much of the @@ -1230,7 +1232,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 935b78c2a..33928cd37 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.cpp b/libevm/SmartVM.cpp index d37abc1bf..12f2f7078 100644 --- a/libevm/SmartVM.cpp +++ b/libevm/SmartVM.cpp @@ -41,7 +41,7 @@ namespace } } -bytesConstRef SmartVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) +bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { auto codeHash = sha3(_ext.code); auto vmKind = VMKind::Interpreter; // default VM @@ -68,7 +68,7 @@ bytesConstRef SmartVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, // TODO: Selected VM must be kept only because it returns reference to its internal memory. // VM implementations should be stateless, without escaping memory reference. m_selectedVM = VMFactory::create(vmKind); - return m_selectedVM->go(io_gas, _ext, _onOp, _steps); + return m_selectedVM->execImpl(io_gas, _ext, _onOp); } } diff --git a/libevm/SmartVM.h b/libevm/SmartVM.h index cc198c0c2..fce7be21e 100644 --- a/libevm/SmartVM.h +++ b/libevm/SmartVM.h @@ -31,7 +31,7 @@ namespace eth class SmartVM: public VMFace { public: - virtual bytesConstRef go(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) override final; private: std::unique_ptr m_selectedVM; diff --git a/libevm/VM.cpp b/libevm/VM.cpp index 8c96a35b2..ed4cdb4dc 100644 --- a/libevm/VM.cpp +++ b/libevm/VM.cpp @@ -45,7 +45,7 @@ static array metrics() return s_ret; } -bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) +bytesConstRef VM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { // Reset leftovers from possible previous run m_curPC = 0; @@ -73,8 +73,7 @@ bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint6 i += _ext.code[i] - (unsigned)Instruction::PUSH1 + 1; } u256 nextPC = m_curPC + 1; - auto osteps = _steps; - for (bool stopped = false; !stopped && _steps--; m_curPC = nextPC, nextPC = m_curPC + 1) + for (uint64_t steps = 0; true; m_curPC = nextPC, nextPC = m_curPC + 1, ++steps) { // INSTRUCTION... Instruction inst = (Instruction)_ext.getCode(m_curPC); @@ -97,7 +96,7 @@ bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint6 auto onOperation = [&]() { if (_onOp) - _onOp(osteps - _steps - 1, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext); + _onOp(steps, inst, newTempSize > m_temp.size() ? (newTempSize - m_temp.size()) / 32 : bigint(0), runGas, io_gas, this, &_ext); }; switch (inst) @@ -670,7 +669,5 @@ bytesConstRef VM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint6 } } - if (_steps == (uint64_t)-1) - BOOST_THROW_EXCEPTION(StepsDone()); return bytesConstRef(); } diff --git a/libevm/VM.h b/libevm/VM.h index 6f984ec5c..9084769cf 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 go(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) override final; u256 curPC() const { return m_curPC; } diff --git a/libevm/VMFace.h b/libevm/VMFace.h index d2a12e0ca..2a9ed808e 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -26,7 +26,6 @@ namespace eth { struct VMException: virtual Exception {}; -struct StepsDone: virtual VMException {}; struct BreakPointHit: virtual VMException {}; struct BadInstruction: virtual VMException {}; struct BadJumpDestination: virtual VMException {}; @@ -43,7 +42,22 @@ public: VMFace(VMFace const&) = delete; VMFace& operator=(VMFace const&) = delete; - virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) = 0; + /// Execute EVM code by VM. + /// + /// @param _out Expected output + void exec(u256& io_gas, ExtVMFace& _ext, bytesRef _out, OnOpFunc const& _onOp = {}) + { + execImpl(io_gas, _ext, _onOp).copyTo(_out); + } + + /// The same as above but returns a copy of full output. + bytes exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}) + { + return execImpl(io_gas, _ext, _onOp).toVector(); + } + + /// VM implementation + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) = 0; }; } diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index fe86908fb..fadf2776a 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -437,7 +437,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat { assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); int instructionNum = 1 + m_stackHeight - _fromPosition; - assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); appendItem(AssemblyItem(dupInstruction(instructionNum), _location)); m_stack[m_stackHeight] = m_stack[_fromPosition]; @@ -450,7 +450,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons if (_fromPosition == m_stackHeight) return; int instructionNum = m_stackHeight - _fromPosition; - assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep."); + assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables."); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); appendItem(AssemblyItem(swapInstruction(instructionNum), _location)); diff --git a/libsolidity/ArrayUtils.cpp b/libsolidity/ArrayUtils.cpp index 531ab8af1..b770b815c 100644 --- a/libsolidity/ArrayUtils.cpp +++ b/libsolidity/ArrayUtils.cpp @@ -168,7 +168,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... - solAssert(2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); + solAssert( + 2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, + "Stack too deep, try removing local variables." + ); // fetch target storage reference m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack()); if (haveByteOffsetTarget) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 40ed1fd8e..0a75e55a9 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -367,7 +367,7 @@ bool Compiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector(c_localVariablesSize, -1); - solAssert(stackLayout.size() <= 17, "Stack too deep."); + solAssert(stackLayout.size() <= 17, "Stack too deep, try removing local variables."); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index e3d8f7f90..3549ef98d 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -142,22 +142,25 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) solAssert(stackPosition >= size, "Variable size and position mismatch."); // move variable starting from its top end in the stack if (stackPosition - size + 1 > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) - << errinfo_comment("Stack too deep.")); + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_variable.getLocation()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); for (unsigned i = 0; i < size; ++i) m_context << eth::swapInstruction(stackPosition - size + 1) << eth::Instruction::POP; } void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) { - solAssert(_stackDepth <= 16, "Stack too deep."); + solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables."); for (unsigned i = 0; i < _itemSize; ++i) m_context << eth::dupInstruction(_stackDepth); } void CompilerUtils::moveToStackTop(unsigned _stackDepth) { - solAssert(_stackDepth <= 15, "Stack too deep."); + solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables."); for (unsigned i = 0; i < _stackDepth; ++i) m_context << eth::swapInstruction(1 + i); } diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index bac967d84..ba80a8ea2 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -263,7 +263,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); if (lvalueSize > 0) { - solAssert(itemSize + lvalueSize <= 16, "Stack too deep."); + solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables."); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index 38efb2a73..b684e55a1 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -42,8 +42,11 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const { unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_location) << + errinfo_comment("Stack too deep, try removing local variables.") + ); for (unsigned i = 0; i < m_size; ++i) m_context << eth::dupInstruction(stackPos + 1); } @@ -52,8 +55,11 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo { unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; if (stackDiff > 16) - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_location) << + errinfo_comment("Stack too deep, try removing local variables.") + ); else if (stackDiff > 0) for (unsigned i = 0; i < m_size; ++i) m_context << eth::swapInstruction(stackDiff) << eth::Instruction::POP; @@ -65,8 +71,11 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const { unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset); if (stackDiff > 16) - BOOST_THROW_EXCEPTION(CompilerError() - << errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep.")); + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_location) << + errinfo_comment("Stack too deep, try removing local variables.") + ); solAssert(stackDiff >= m_size - 1, ""); for (unsigned i = 0; i < m_size; ++i) m_context << u256(0) << eth::swapInstruction(stackDiff + 1 - i) @@ -204,7 +213,10 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off StorageItem(m_context, *memberType).retrieveValue(_location, true); // stack: source_ref source_off target_ref target_off member_offset source_value... - solAssert(4 + memberType->getSizeOnStack() <= 16, "Stack too deep."); + solAssert( + 4 + memberType->getSizeOnStack() <= 16, + "Stack too deep, try removing local varibales." + ); m_context << eth::dupInstruction(4 + memberType->getSizeOnStack()) << eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index aa469d8a0..7c0498ef0 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.setResultRecipient(er); execution.initialize(t); execution.execute(); std::vector machineStates; @@ -198,7 +200,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) { @@ -225,7 +226,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/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 860b4c3b8..e65c602ab 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -412,8 +412,6 @@ bool CommandLineInterface::processInput() // TODO: Perhaps we should not compile unless requested bool optimize = m_args["optimize"].as(); unsigned runs = m_args["optimize-runs"].as(); - if (m_args.count("optimize-runs")) - optimize = true; m_compiler->compile(optimize, runs); } catch (ParserError const& _exception) diff --git a/test/fuzzTesting/checkRandomVMTest.cpp b/test/fuzzTesting/checkRandomVMTest.cpp index 13d677e17..a6ade07f1 100644 --- a/test/fuzzTesting/checkRandomVMTest.cpp +++ b/test/fuzzTesting/checkRandomVMTest.cpp @@ -98,9 +98,9 @@ bool doVMTest(mValue& _v) try { auto vm = eth::VMFactory::create(); - output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes(); + output = vm->exec(fev.gas, fev, fev.simpleTrace()); } - catch (eth::VMException) + catch (eth::VMException const&) { cnote << "Safe VM Exception"; vmExceptionOccured = true; diff --git a/test/fuzzTesting/createRandomVMTest.cpp b/test/fuzzTesting/createRandomVMTest.cpp index 3196590d4..93040d6de 100644 --- a/test/fuzzTesting/createRandomVMTest.cpp +++ b/test/fuzzTesting/createRandomVMTest.cpp @@ -160,7 +160,7 @@ void doMyTests(json_spirit::mValue& _v) bool vmExceptionOccured = false; try { - output = vm->go(fev.gas, fev, fev.simpleTrace()).toBytes(); + output = vm->exec(fev.gas, fev, fev.simpleTrace()); } catch (eth::VMException const& _e) { diff --git a/test/libevm/vm.cpp b/test/libevm/vm.cpp index 2c56b7ada..d52c650f9 100644 --- a/test/libevm/vm.cpp +++ b/test/libevm/vm.cpp @@ -330,12 +330,10 @@ void doVMTests(json_spirit::mValue& v, bool _fillin) { 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.gas, fev, vmtrace); + output = vm->exec(fev.gas, fev, vmtrace); } - output = outputRef.toBytes(); } catch (VMException const&) { diff --git a/test/libsolidity/solidityExecutionFramework.h b/test/libsolidity/solidityExecutionFramework.h index e81b4d7b5..44590b1c8 100644 --- a/test/libsolidity/solidityExecutionFramework.h +++ b/test/libsolidity/solidityExecutionFramework.h @@ -148,6 +148,8 @@ protected: { m_state.addBalance(m_sender, _value); // just in case eth::Executive executive(m_state, eth::LastHashes(), 0); + eth::ExecutionResult res; + executive.setResultRecipient(res); eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : @@ -176,7 +178,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(); }