From 1619d230c3835c008a7d80e1f2bf41a5ba065dc1 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 18 Mar 2015 12:56:50 +0100 Subject: [PATCH] Extensive pathway for reporting transaction execution results. --- alethzero/DappLoader.cpp | 7 ++-- alethzero/MainWin.cpp | 8 ++--- evmjit/libevmjit-cpp/JitVM.cpp | 4 +-- evmjit/libevmjit/Common.h | 2 +- evmjit/libevmjit/Compiler.cpp | 2 +- libethereum/Client.cpp | 41 +++++++++++++++------ libethereum/Client.h | 16 +++++---- libethereum/Executive.cpp | 22 ++++++++++-- libethereum/Executive.h | 8 +++-- libethereum/Interface.h | 6 +++- libethereum/State.cpp | 65 ++++++++++++++++------------------ libethereum/State.h | 14 +++++--- libethereum/Transaction.cpp | 21 +++++++++++ libethereum/Transaction.h | 40 +++++++++++++++++++++ libevm/VM.h | 2 +- libevm/VMFace.h | 2 +- mix/ClientModel.cpp | 2 +- mix/MachineStates.h | 3 +- mix/MixClient.cpp | 24 ++++++++++--- mix/MixClient.h | 3 +- test/checkRandomStateTest.cpp | 2 +- test/createRandomStateTest.cpp | 2 +- test/state.cpp | 2 +- 23 files changed, 214 insertions(+), 84 deletions(-) diff --git a/alethzero/DappLoader.cpp b/alethzero/DappLoader.cpp index 6482b691d..821629906 100644 --- a/alethzero/DappLoader.cpp +++ b/alethzero/DappLoader.cpp @@ -72,20 +72,19 @@ DappLocation DappLoader::resolveAppUri(QString const& _uri) string32 name = ZeroString32; QByteArray utf8 = parts[partIndex].toUtf8(); std::copy(utf8.data(), utf8.data() + utf8.size(), name.data()); - address = abiOut
(web3()->ethereum()->call(address, abiIn("addr(string32)", name))); + address = abiOut
(web3()->ethereum()->call(address, abiIn("addr(string32)", name)).output); domainParts.append(parts[partIndex]); if (!address) { //we have the address of the last part, try to get content hash - contentHash = abiOut(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name))); + contentHash = abiOut(web3()->ethereum()->call(lastAddress, abiIn("content(string32)", name)).output); if (!contentHash) throw dev::Exception() << errinfo_comment("Can't resolve address"); } ++partIndex; } - - string32 contentUrl = abiOut(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash))); + string32 contentUrl = abiOut(web3()->ethereum()->call(c_urlHint, abiIn("url(hash256)", contentHash)).output); QString domain = domainParts.join('/'); parts.erase(parts.begin(), parts.begin() + partIndex); QString path = parts.join('/'); diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 091e4ce98..967bc6b56 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -284,12 +284,12 @@ void Main::installWatches() Address Main::getNameReg() const { - return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)1))); + return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)1)).output); } Address Main::getCurrencies() const { - return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)3))); + return abiOut
(ethereum()->call(c_newConfig, abiIn("lookup(uint256)", (u256)3)).output); } void Main::installNameRegWatch() @@ -470,7 +470,7 @@ QString Main::pretty(dev::Address _a) const if (g_newNameReg) { - QString s = QString::fromStdString(toString(abiOut(ethereum()->call(g_newNameReg, abiIn("nameOf(address)", _a))))); + QString s = QString::fromStdString(toString(abiOut(ethereum()->call(g_newNameReg, abiIn("nameOf(address)", _a)).output))); if (s.size()) return s; } @@ -575,7 +575,7 @@ Address Main::fromString(QString const& _n) const auto g_newNameReg = getNameReg(); if (g_newNameReg) { - Address a = abiOut
(ethereum()->call(g_newNameReg, abiIn("addressOf(string32)", ::fromString(_n.toStdString())))); + Address a = abiOut
(ethereum()->call(g_newNameReg, abiIn("addressOf(string32)", ::fromString(_n.toStdString()))).output); if (a) return a; } diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index e28fcd39f..84cc41dd1 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -69,8 +69,8 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) BOOST_THROW_EXCEPTION(BadJumpDestination()); case ReturnCode::OutOfGas: BOOST_THROW_EXCEPTION(OutOfGas()); - case ReturnCode::StackTooSmall: - BOOST_THROW_EXCEPTION(StackTooSmall()); + case ReturnCode::StackUnderflow: + BOOST_THROW_EXCEPTION(StackUnderflow()); case ReturnCode::BadInstruction: BOOST_THROW_EXCEPTION(BadInstruction()); case ReturnCode::LinkerWorkaround: // never happens diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index 62731292f..e564e0702 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -33,7 +33,7 @@ enum class ReturnCode // Standard error codes OutOfGas = -1, - StackTooSmall = -2, + StackUnderflow = -2, BadJumpDestination = -3, BadInstruction = -4, Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index de48e8ef9..2039b5d10 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -516,7 +516,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto val = stack.pop(); static_cast(val); // Generate a dummy use of val to make sure that a get(0) will be emitted at this point, - // so that StackTooSmall will be thrown + // so that StackUnderflow will be thrown // m_builder.CreateICmpEQ(val, val, "dummy"); break; } diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index 4d10679dd..fb29ffb6a 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -491,9 +491,9 @@ void Client::submitTransaction(Secret _secret, u256 _value, Address _dest, bytes m_tq.attemptImport(t.rlp()); } -bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) +ExecutionResult Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) { - bytes out; + ExecutionResult ret; try { u256 n; @@ -505,18 +505,41 @@ bytes Client::call(Secret _secret, u256 _value, Address _dest, bytes const& _dat n = temp.transactionsFrom(toAddress(_secret)); } Transaction t(_value, _gasPrice, _gas, _dest, _data, n, _secret); - u256 gasUsed = temp.execute(m_bc, t.rlp(), &out, false); - (void)gasUsed; // TODO: do something with gasused which it returns. + ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); } catch (...) { // TODO: Some sort of notification of failure. } - return out; + return ret; } -bytes Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice) +ExecutionResult Client::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) { + ExecutionResult ret; + try + { + u256 n; + State temp; + // cdebug << "Nonce at " << toAddress(_secret) << " pre:" << m_preMine.transactionsFrom(toAddress(_secret)) << " post:" << m_postMine.transactionsFrom(toAddress(_secret)); + { + ReadGuard l(x_stateDB); + temp = asOf(_blockNumber); + n = temp.transactionsFrom(toAddress(_secret)); + } + Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + ret = temp.execute(m_bc, t.rlp(), Permanence::Reverted); + } + catch (...) + { + // TODO: Some sort of notification of failure. + } + return ret; +} + +ExecutionResult Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u256 _gasPrice) +{ + ExecutionResult ret; try { State temp; @@ -527,16 +550,14 @@ bytes Client::call(Address _dest, bytes const& _data, u256 _gas, u256 _value, u2 } Executive e(temp, LastHashes(), 0); if (!e.call(_dest, _dest, Address(), _value, _gasPrice, &_data, _gas, Address())) - { e.go(); - return e.out().toBytes(); - } + ret = e.executionResult(); } catch (...) { // TODO: Some sort of notification of failure. } - return bytes(); + return ret; } Address Client::submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) diff --git a/libethereum/Client.h b/libethereum/Client.h index 6048715c4..426c78ca7 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -212,23 +212,27 @@ public: void setGasPricer(std::shared_ptr _gp) { m_gp = _gp; } /// Submits the given message-call transaction. - virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + virtual void submitTransaction(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; /// Submits a new contract-creation transaction. /// @returns the new contract's address (assuming it all goes through). - virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo); + virtual Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas = 10000, u256 _gasPrice = 10 * szabo) override; /// Injects the RLP-encoded transaction given by the _rlp into the transaction queue directly. - virtual void inject(bytesConstRef _rlp); + virtual void inject(bytesConstRef _rlp) override; /// Blocks until all pending transactions have been processed. - virtual void flushTransactions(); + virtual void flushTransactions() override; /// Makes the given call. Nothing is recorded into the state. - virtual bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0); + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) override; + + /// Does the given creation. Nothing is recorded into the state. + /// @returns the pair of the Address of the created contract together with its code. + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) override; /// Makes the given call. Nothing is recorded into the state. This cheats by creating a null address and endowing it with a lot of ETH. - virtual bytes call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether); + ExecutionResult call(Address _dest, bytes const& _data = bytes(), u256 _gas = 125000, u256 _value = 0, u256 _gasPrice = 1 * ether); // Informational stuff diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index e009b49fb..b44caf4ee 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -53,7 +53,16 @@ void Executive::accrueSubState(SubState& _parentContext) bool Executive::setup(bytesConstRef _rlp) { // Entry point for a user-executed transaction. - m_t = Transaction(_rlp, CheckSignature::Sender); + try + { + m_t = Transaction(_rlp, CheckSignature::Sender); + } + catch (...) + { + clog(StateDetail) << "Invalid Signature"; + m_excepted = TransactionException::InvalidSignature; + throw; + } return setup(); } @@ -66,6 +75,7 @@ bool Executive::setup() if (m_t.nonce() != nonceReq) { clog(StateDetail) << "Invalid Nonce: Require" << nonceReq << " Got" << m_t.nonce(); + m_excepted = TransactionException::InvalidNonce; BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce())); } @@ -120,7 +130,7 @@ bool Executive::call(Address _receiveAddress, Address _codeAddress, Address _sen if (_gas < g) { m_endGas = 0; - m_excepted = true; + m_excepted = TransactionException::OutOfGasBase; } else { @@ -200,9 +210,15 @@ bool Executive::go(OnOpFunc const& _onOp) if (m_isCreation) { if (m_out.size() * c_createDataGas <= m_endGas) + { + m_codeDeposit = CodeDeposit::Success; m_endGas -= m_out.size() * c_createDataGas; + } else + { + m_codeDeposit = CodeDeposit::Failed; m_out.reset(); + } m_s.m_cache[m_newAddress].setCode(m_out.toBytes()); } } @@ -214,7 +230,7 @@ bool Executive::go(OnOpFunc const& _onOp) { clog(StateSafeExceptions) << "Safe VM Exception. " << diagnostic_information(_e); m_endGas = 0; - m_excepted = true; + m_excepted = toTransactionException(_e); m_ext->revert(); } catch (Exception const& _e) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 2e89f0623..d04a39da8 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -101,7 +101,10 @@ public: /// @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; } + bool excepted() const { return m_excepted != TransactionException::None; } + + /// Get the above in an amalgamated fashion. + ExecutionResult executionResult() const { return ExecutionResult(gasUsed(), m_excepted, m_newAddress, m_out, m_codeDeposit); } private: bool setup(); @@ -116,7 +119,8 @@ private: 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. - bool m_excepted = false; ///< True if the VM execution resulted in an exception. + 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. Transaction m_t; ///< The original transaction. Set by setup(). diff --git a/libethereum/Interface.h b/libethereum/Interface.h index 83305af3e..dfbd47704 100644 --- a/libethereum/Interface.h +++ b/libethereum/Interface.h @@ -73,7 +73,11 @@ public: virtual void flushTransactions() = 0; /// Makes the given call. Nothing is recorded into the state. - virtual bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) = 0; + virtual ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) = 0; + + /// Does the given creation. Nothing is recorded into the state. + /// @returns the pair of the Address of the created contract together with its code. + virtual ExecutionResult create(Secret _secret, u256 _value, bytes const& _data = bytes(), u256 _gas = 10000, u256 _gasPrice = 10 * szabo, int _blockNumber = 0) = 0; // [STATE-QUERY API] diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 9bfc0c08c..47e6542db 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -1048,18 +1048,18 @@ LastHashes State::getLastHashes(BlockChain const& _bc, unsigned _n) const return ret; } -u256 State::execute(BlockChain const& _bc, bytes const& _rlp, bytes* o_output, bool _commit) +ExecutionResult State::execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p) { - return execute(getLastHashes(_bc, _bc.number()), &_rlp, o_output, _commit); + return execute(getLastHashes(_bc, _bc.number()), &_rlp, _p); } -u256 State::execute(BlockChain const& _bc, bytesConstRef _rlp, bytes* o_output, bool _commit) +ExecutionResult State::execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p) { - return execute(getLastHashes(_bc, _bc.number()), _rlp, o_output, _commit); + return execute(getLastHashes(_bc, _bc.number()), _rlp, _p); } // TODO: maintain node overlay revisions for stateroots -> each commit gives a stateroot + OverlayDB; allow overlay copying for rewind operations. -u256 State::execute(LastHashes const& _lh, bytesConstRef _rlp, bytes* o_output, bool _commit) +ExecutionResult State::execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p) { #ifndef ETH_RELEASE commit(); // get an updated hash @@ -1093,41 +1093,38 @@ u256 State::execute(LastHashes const& _lh, bytesConstRef _rlp, bytes* o_output, ctrace << old.diff(*this); #endif - if (o_output) - *o_output = e.out().toBytes(); - - if (!_commit) - { + if (_p == Permanence::Reverted) m_cache.clear(); - return e.gasUsed(); - } - - commit(); - -#if ETH_PARANOIA && !ETH_FATDB - ctrace << "Executed; now" << rootHash(); - ctrace << old.diff(*this); - - paranoia("after execution commit.", true); - - if (e.t().receiveAddress()) + else { - EnforceRefs r(m_db, true); - if (storageRoot(e.t().receiveAddress()) && m_db.lookup(storageRoot(e.t().receiveAddress())).empty()) + commit(); + +#if ETH_PARANOIA && !ETH_FATDB + ctrace << "Executed; now" << rootHash(); + ctrace << old.diff(*this); + + paranoia("after execution commit.", true); + + if (e.t().receiveAddress()) { - cwarn << "TRIE immediately after execution; no node for receiveAddress"; - BOOST_THROW_EXCEPTION(InvalidTrie()); + EnforceRefs r(m_db, true); + if (storageRoot(e.t().receiveAddress()) && m_db.lookup(storageRoot(e.t().receiveAddress())).empty()) + { + cwarn << "TRIE immediately after execution; no node for receiveAddress"; + BOOST_THROW_EXCEPTION(InvalidTrie()); + } } - } #endif + + // TODO: CHECK TRIE after level DB flush to make sure exactly the same. + + // Add to the user-originated transactions that we've executed. + m_transactions.push_back(e.t()); + m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs())); + m_transactionSet.insert(e.t().sha3()); + } - // TODO: CHECK TRIE after level DB flush to make sure exactly the same. - - // Add to the user-originated transactions that we've executed. - m_transactions.push_back(e.t()); - m_receipts.push_back(TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs())); - m_transactionSet.insert(e.t().sha3()); - return e.gasUsed(); + return e.executionResult(); } State State::fromPending(unsigned _i) const diff --git a/libethereum/State.h b/libethereum/State.h index bfa60e452..36d3e7c09 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -84,6 +84,12 @@ protected: u256 bid(TransactionPriority = TransactionPriority::Medium) const override { return 10 * szabo; } }; +enum class Permanence +{ + Reverted, + Committed +}; + /** * @brief Model of the current state of the ledger. * Maintains current ledger (m_current) as a fast hash-map. This is hashed only when required (i.e. to create or verify a block). @@ -185,10 +191,10 @@ public: /// Execute a given transaction. /// This will append @a _t to the transaction list and change the state accordingly. - u256 execute(BlockChain const& _bc, bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true); - u256 execute(BlockChain const& _bc, bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); - u256 execute(LastHashes const& _lh, bytes const& _rlp, bytes* o_output = nullptr, bool _commit = true) { return execute(_lh, &_rlp, o_output, _commit); } - u256 execute(LastHashes const& _lh, bytesConstRef _rlp, bytes* o_output = nullptr, bool _commit = true); + ExecutionResult execute(BlockChain const& _bc, bytes const& _rlp, Permanence _p = Permanence::Committed); + ExecutionResult execute(BlockChain const& _bc, bytesConstRef _rlp, Permanence _p = Permanence::Committed); + ExecutionResult execute(LastHashes const& _lh, bytes const& _rlp, Permanence _p = Permanence::Committed) { return execute(_lh, &_rlp, _p); } + ExecutionResult execute(LastHashes const& _lh, bytesConstRef _rlp, Permanence _p = Permanence::Committed); /// Get the remaining gas limit in this block. u256 gasLimitRemaining() const { return m_currentBlock.gasLimit - gasUsed(); } diff --git a/libethereum/Transaction.cpp b/libethereum/Transaction.cpp index a89083648..3a092bf25 100644 --- a/libethereum/Transaction.cpp +++ b/libethereum/Transaction.cpp @@ -21,8 +21,10 @@ #include #include +#include #include #include +#include #include "Transaction.h" using namespace std; using namespace dev; @@ -30,6 +32,25 @@ using namespace dev::eth; #define ETH_ADDRESS_DEBUG 0 +std::ostream& dev::eth::operator<<(std::ostream& _out, ExecutionResult const& _er) +{ + _out << "{" << _er.gasUsed << ", " << _er.newAddress << ", " << toHex(_er.output) << "}"; + return _out; +} + +TransactionException dev::eth::toTransactionException(VMException const& _e) +{ + if (!!dynamic_cast(&_e)) + return TransactionException::BadInstruction; + if (!!dynamic_cast(&_e)) + return TransactionException::BadJumpDestination; + if (!!dynamic_cast(&_e)) + return TransactionException::OutOfGas; + if (!!dynamic_cast(&_e)) + return TransactionException::StackUnderflow; + return TransactionException::Unknown; +} + Transaction::Transaction(bytesConstRef _rlpData, CheckSignature _checkSig) { int field = 0; diff --git a/libethereum/Transaction.h b/libethereum/Transaction.h index 7dd28f7c6..85543a3fc 100644 --- a/libethereum/Transaction.h +++ b/libethereum/Transaction.h @@ -44,6 +44,46 @@ enum class CheckSignature Sender }; +enum class TransactionException +{ + None = 0, + Unknown, + InvalidSignature, + InvalidNonce, + NotEnoughCash, + OutOfGasBase, ///< Too little gas to pay for the base transaction cost. + BlockGasLimitReached, + BadInstruction, + BadJumpDestination, + OutOfGas, ///< Ran out of gas executing code of the transaction. + StackUnderflow +}; + +enum class CodeDeposit +{ + None = 0, + Failed, + Success +}; + +class VMException; + +TransactionException toTransactionException(VMException const& _e); + +/// Description of the result of executing a transaction. +struct ExecutionResult +{ + ExecutionResult() = default; + ExecutionResult(u256 _gasUsed, TransactionException _excepted, Address _newAddress, bytesConstRef _output, CodeDeposit _codeDeposit): gasUsed(_gasUsed), excepted(_excepted), newAddress(_newAddress), output(_output.toBytes()), codeDeposit(_codeDeposit) {} + u256 gasUsed; + TransactionException excepted = TransactionException::Unknown; + Address newAddress; + bytes output; + CodeDeposit codeDeposit = CodeDeposit::None; +}; + +std::ostream& operator<<(std::ostream& _out, ExecutionResult const& _er); + /// Encodes a transaction, ready to be exported to or freshly imported from RLP. class Transaction { diff --git a/libevm/VM.h b/libevm/VM.h index f2c773e4b..3efb3b119 100644 --- a/libevm/VM.h +++ b/libevm/VM.h @@ -56,7 +56,7 @@ public: virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; - void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackTooSmall() << RequirementError((bigint)_n, (bigint)m_stack.size())); } } + void require(u256 _n) { if (m_stack.size() < _n) { if (m_onFail) m_onFail(); BOOST_THROW_EXCEPTION(StackUnderflow() << RequirementError((bigint)_n, (bigint)m_stack.size())); } } void requireMem(unsigned _n) { if (m_temp.size() < _n) { m_temp.resize(_n); } } u256 curPC() const { return m_curPC; } diff --git a/libevm/VMFace.h b/libevm/VMFace.h index f8c20feb1..7b99b5bfd 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -31,7 +31,7 @@ struct BreakPointHit: virtual VMException {}; struct BadInstruction: virtual VMException {}; struct BadJumpDestination: virtual VMException {}; struct OutOfGas: virtual VMException {}; -struct StackTooSmall: virtual VMException {}; +struct StackUnderflow: virtual VMException {}; /// EVM Virtual Machine interface class VMFace diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index 6f3b8c831..76c659fba 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -534,7 +534,7 @@ void ClientModel::onNewTransaction() { function = funcDef->name(); ContractCallDataEncoder encoder; - QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.returnValue); + QStringList returnValues = encoder.decode(funcDef->returnParameters(), tr.result.output); returned += "("; returned += returnValues.join(", "); returned += ")"; diff --git a/mix/MachineStates.h b/mix/MachineStates.h index 310d5cacd..a97ee0787 100644 --- a/mix/MachineStates.h +++ b/mix/MachineStates.h @@ -29,6 +29,7 @@ along with cpp-ethereum. If not, see . #include #include #include +#include #include namespace dev @@ -74,7 +75,7 @@ namespace mix std::vector machineStates; std::vector transactionData; std::vector executionCode; - bytes returnValue; + dev::eth::ExecutionResult result; dev::Address address; dev::Address sender; dev::Address contractAddress; diff --git a/mix/MixClient.cpp b/mix/MixClient.cpp index 74e8ca9ef..a8da07a55 100644 --- a/mix/MixClient.cpp +++ b/mix/MixClient.cpp @@ -175,7 +175,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c execution.finalize(); ExecutionResult d; - d.returnValue = execution.out().toVector(); + d.result = execution.executionResult(); d.machineStates = machineStates; d.executionCode = std::move(codes); d.transactionData = std::move(data); @@ -191,7 +191,7 @@ void MixClient::executeTransaction(Transaction const& _t, State& _state, bool _c // execute on a state if (!_call) { - _state.execute(lastHashes, rlp, nullptr, true); + _state.execute(lastHashes, rlp); // collect watches h256Set changed; Guard l(m_filterLock); @@ -276,7 +276,7 @@ void MixClient::flushTransactions() { } -bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) +dev::eth::ExecutionResult MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) { u256 n; State temp; @@ -289,7 +289,23 @@ bytes MixClient::call(Secret _secret, u256 _value, Address _dest, bytes const& _ bytes rlp = t.rlp(); WriteGuard lw(x_state); //TODO: lock is required only for last execution state executeTransaction(t, temp, true); - return lastExecution().returnValue; + return lastExecution().result; +} + +dev::eth::ExecutionResult MixClient::create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) +{ + u256 n; + State temp; + { + ReadGuard lr(x_state); + temp = asOf(_blockNumber); + n = temp.transactionsFrom(toAddress(_secret)); + } + Transaction t(_value, _gasPrice, _gas, _data, n, _secret); + bytes rlp = t.rlp(); + WriteGuard lw(x_state); //TODO: lock is required only for last execution state + executeTransaction(t, temp, true); + return lastExecution().result; } u256 MixClient::balanceAt(Address _a, int _block) const diff --git a/mix/MixClient.h b/mix/MixClient.h index 575871899..c8024a011 100644 --- a/mix/MixClient.h +++ b/mix/MixClient.h @@ -52,7 +52,8 @@ public: Address submitTransaction(Secret _secret, u256 _endowment, bytes const& _init, u256 _gas, u256 _gasPrice) override; void inject(bytesConstRef _rlp) override; void flushTransactions() override; - bytes call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) override; + dev::eth::ExecutionResult call(Secret _secret, u256 _value, Address _dest, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) override; + dev::eth::ExecutionResult create(Secret _secret, u256 _value, bytes const& _data, u256 _gas, u256 _gasPrice, int _blockNumber) override; u256 balanceAt(Address _a, int _block) const override; u256 countAt(Address _a, int _block) const override; u256 stateAt(Address _a, u256 _l, int _block) const override; diff --git a/test/checkRandomStateTest.cpp b/test/checkRandomStateTest.cpp index 17e785f60..0fa7233b4 100644 --- a/test/checkRandomStateTest.cpp +++ b/test/checkRandomStateTest.cpp @@ -87,7 +87,7 @@ bool doStateTest(mValue& _v) try { - theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx, &output); + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; } catch (Exception const& _e) { diff --git a/test/createRandomStateTest.cpp b/test/createRandomStateTest.cpp index 13b622bb1..c03deca80 100644 --- a/test/createRandomStateTest.cpp +++ b/test/createRandomStateTest.cpp @@ -162,7 +162,7 @@ void doStateTests(json_spirit::mValue& _v) try { - theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), tx, &output); + output = theState.execute(test::lastHashes(importer.m_environment.currentBlock.number), tx).output; } catch (Exception const& _e) { diff --git a/test/state.cpp b/test/state.cpp index 162ae5f34..4ab59f7a1 100644 --- a/test/state.cpp +++ b/test/state.cpp @@ -63,7 +63,7 @@ void doStateTests(json_spirit::mValue& v, bool _fillin) try { Listener::ExecTimeGuard guard{i.first}; - theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx, &output); + output = theState.execute(lastHashes(importer.m_environment.currentBlock.number), tx).output; } catch (Exception const& _e) {