diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 0de1c9a746d..ad1533102ca 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -77,6 +77,7 @@ DEV_SIMPLE_EXCEPTION(InvalidUncleParentHash); DEV_SIMPLE_EXCEPTION(InvalidNumber); DEV_SIMPLE_EXCEPTION(InvalidZeroSignatureTransaction); DEV_SIMPLE_EXCEPTION(InvalidTransactionReceiptFormat); +DEV_SIMPLE_EXCEPTION(TransactionReceiptVersionError); DEV_SIMPLE_EXCEPTION(BlockNotFound); DEV_SIMPLE_EXCEPTION(UnknownParent); DEV_SIMPLE_EXCEPTION(AddressAlreadyUsed); diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 068cc899c53..dcbffb1fcb7 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -864,7 +864,14 @@ bool Block::sealBlock(bytesConstRef _header) h256 Block::stateRootBeforeTx(unsigned _i) const { _i = min(_i, m_transactions.size()); - return (_i > 0 ? receipt(_i - 1).stateRoot() : m_previousBlock.stateRoot()); + try + { + return (_i > 0 ? receipt(_i - 1).stateRoot() : m_previousBlock.stateRoot()); + } + catch (TransactionReceiptVersionError const&) + { + return {}; + } } LogBloom Block::logBloom() const diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 2b5e893d665..0d75aad1f2c 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -482,7 +482,7 @@ bool Executive::go(OnOpFunc const& _onOp) return true; } -void Executive::finalize() +bool Executive::finalize() { // Accumulate refunds for suicides. if (m_ext) @@ -517,6 +517,7 @@ void Executive::finalize() m_res->newAddress = m_newAddress; m_res->gasRefunded = m_ext ? m_ext->sub.refunds : 0; } + return (m_excepted == TransactionException::None); } void Executive::revert() diff --git a/libethereum/Executive.h b/libethereum/Executive.h index fee6373ee67..4d0004b5b87 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -131,7 +131,8 @@ class Executive void initialize(Transaction const& _transaction); /// Finalise a transaction previously set up with initialize(). /// @warning Only valid after initialize() and execute(), and possibly go(). - void finalize(); + /// @returns true if the outermost execution halted normally, false if exceptionally halted. + bool finalize(); /// Begins execution of a transaction. You must call finalize() following this. /// @returns true if the transaction is done, false if go() must be called. bool execute(); diff --git a/libethereum/State.cpp b/libethereum/State.cpp index eab3ae1f9c1..257cb826003 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -51,13 +51,14 @@ const char* StateChat::name() { return EthViolet "⚙" EthWhite " ◌"; } namespace { -void executeTransaction(Executive& _e, Transaction const& _t, OnOpFunc const& _onOp) +/// @returns true when normally halted; false when exceptionally halted. +bool executeTransaction(Executive& _e, Transaction const& _t, OnOpFunc const& _onOp) { _e.initialize(_t); if (!_e.execute()) _e.go(_onOp); - _e.finalize(); + return _e.finalize(); } } @@ -564,7 +565,7 @@ std::pair State::execute(EnvInfo const& _en e.setResultRecipient(res); u256 const startGasUsed = _envInfo.gasUsed(); - executeTransaction(e, _t, onOp); + bool const statusCode = executeTransaction(e, _t, onOp); bool removeEmptyAccounts = false; switch (_p) @@ -581,7 +582,7 @@ std::pair State::execute(EnvInfo const& _en } TransactionReceipt const receipt = _envInfo.number() >= _sealEngine.chainParams().u256Param("metropolisForkBlock") ? - TransactionReceipt(startGasUsed + e.gasUsed(), e.logs()) : + TransactionReceipt(statusCode, startGasUsed + e.gasUsed(), e.logs()) : TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()); return make_pair(res, receipt); } diff --git a/libethereum/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp index 8b64c7871e2..4b7152a3be9 100644 --- a/libethereum/TransactionReceipt.cpp +++ b/libethereum/TransactionReceipt.cpp @@ -22,6 +22,8 @@ #include "TransactionReceipt.h" #include +#include + using namespace std; using namespace dev; using namespace dev::eth; @@ -29,25 +31,35 @@ using namespace dev::eth; TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); - if (!r.isList() || r.itemCount() < 3 || r.itemCount() > 4) + if (!r.isList() || r.itemCount() != 4) BOOST_THROW_EXCEPTION(InvalidTransactionReceiptFormat()); - - size_t gasUsedIndex = 0; - if (r.itemCount() == 4) - { - m_stateRoot = (h256)r[0]; - gasUsedIndex = 1; - } - m_gasUsed = (u256)r[gasUsedIndex]; - m_bloom = (LogBloom)r[gasUsedIndex + 1]; - for (auto const& i : r[gasUsedIndex + 2]) + if (!r[0].isData()) + BOOST_THROW_EXCEPTION(InvalidTransactionReceiptFormat()); + + if (r[0].size() == 32) + m_statusCodeOrStateRoot = (h256)r[0]; + else if (r[0].size() == 1) + m_statusCodeOrStateRoot = (uint8_t)r[0]; + else + BOOST_THROW_EXCEPTION(InvalidTransactionReceiptFormat()); + + m_gasUsed = (u256)r[1]; + m_bloom = (LogBloom)r[2]; + for (auto const& i : r[3]) m_log.emplace_back(i); } -TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): - m_stateRoot(_root), +TransactionReceipt::TransactionReceipt(h256 const& _root, u256 const& _gasUsed, LogEntries const& _log): + m_statusCodeOrStateRoot(_root), + m_gasUsed(_gasUsed), + m_bloom(eth::bloom(_log)), + m_log(_log) +{} + +TransactionReceipt::TransactionReceipt(uint8_t _status, u256 const& _gasUsed, LogEntries const& _log): + m_statusCodeOrStateRoot(_status), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) @@ -55,21 +67,44 @@ TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries con void TransactionReceipt::streamRLP(RLPStream& _s) const { - if (m_stateRoot) - _s.appendList(4) << m_stateRoot; + _s.appendList(4); + if (hasStatusCode()) + _s << statusCode(); else - _s.appendList(3); - + _s << stateRoot(); _s << m_gasUsed << m_bloom; - _s.appendList(m_log.size()); for (LogEntry const& l: m_log) l.streamRLP(_s); } +bool TransactionReceipt::hasStatusCode() const +{ + return m_statusCodeOrStateRoot.which() == 0; +} + +uint8_t TransactionReceipt::statusCode() const +{ + if (hasStatusCode()) + return boost::get(m_statusCodeOrStateRoot); + else + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); +} + +h256 const& TransactionReceipt::stateRoot() const +{ + if (hasStatusCode()) + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); + else + return boost::get(m_statusCodeOrStateRoot); +} + std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionReceipt const& _r) { - _out << "Root: " << _r.stateRoot() << std::endl; + if (_r.hasStatusCode()) + _out << "Status: " << _r.statusCode() << std::endl; + else + _out << "Root: " << _r.stateRoot() << std::endl; _out << "Gas used: " << _r.gasUsed() << std::endl; _out << "Logs: " << _r.log().size() << " entries:" << std::endl; for (LogEntry const& i: _r.log()) diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index 61738c1053b..0cc8d1b7c05 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -26,6 +26,8 @@ #include #include +#include + namespace dev { @@ -33,16 +35,23 @@ namespace eth { /// Transaction receipt, constructed either from RLP representation or from individual values. -/// State Root is optional, m_stateRoot is h256() when it is empty (for transactions after Metropolis) +/// Either a state root or a status code is contained. m_hasStatusCode is true when it contains a status code. /// Empty state root is not included into RLP-encoding. class TransactionReceipt { public: TransactionReceipt(bytesConstRef _rlp); - TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log); - TransactionReceipt(u256 _gasUsed, LogEntries const& _log) : TransactionReceipt(h256(), _gasUsed, _log) {} - - h256 const& stateRoot() const { return m_stateRoot; } + TransactionReceipt(h256 const& _root, u256 const& _gasUsed, LogEntries const& _log); + TransactionReceipt(uint8_t _status, u256 const& _gasUsed, LogEntries const& _log); + + /// @returns true if the receipt has a status code. Otherwise the receipt has a state root (pre-EIP658). + bool hasStatusCode() const; + /// @returns the state root. + /// @throw TransactionReceiptVersionError when the receipt has a status code instead of a state root. + h256 const& stateRoot() const; + /// @returns the status code. + /// @throw TransactionReceiptVersionError when the receipt has a state root instead of a status code. + uint8_t statusCode() const; u256 const& gasUsed() const { return m_gasUsed; } LogBloom const& bloom() const { return m_bloom; } LogEntries const& log() const { return m_log; } @@ -52,7 +61,7 @@ class TransactionReceipt bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); } private: - h256 m_stateRoot; + boost::variant m_statusCodeOrStateRoot; u256 m_gasUsed; LogBloom m_bloom; LogEntries m_log; diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp index beaf1740e1b..895f1dd15f8 100644 --- a/libweb3jsonrpc/JsonHelper.cpp +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -185,7 +185,10 @@ Json::Value toJson(dev::eth::TransactionSkeleton const& _t) Json::Value toJson(dev::eth::TransactionReceipt const& _t) { Json::Value res; - res["stateRoot"] = toJS(_t.stateRoot()); + if (_t.hasStatusCode()) + res["statusCode"] = toJS(_t.statusCode()); + else + res["stateRoot"] = toJS(_t.stateRoot()); res["gasUsed"] = toJS(_t.gasUsed()); res["bloom"] = toJS(_t.bloom()); res["log"] = dev::toJson(_t.log()); diff --git a/test/jsontests b/test/jsontests index 6a3669ff3d8..4b310bd4bcb 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit 6a3669ff3d8059e2b123fdd86803d6474d0e05c1 +Subproject commit 4b310bd4bcbca5c5ed7c05f494246a49a27e6a5d