From a0895aeb0fac4b2defba5cf1b27548b8b42aa664 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Mon, 17 Jul 2017 16:05:15 +0200 Subject: [PATCH 1/4] add status code in transaction receipts after Metropolis EIP658 --- libethcore/Exceptions.h | 1 + libethereum/Executive.cpp | 3 +- libethereum/Executive.h | 3 +- libethereum/State.cpp | 9 +++-- libethereum/TransactionReceipt.cpp | 60 ++++++++++++++++++++++-------- libethereum/TransactionReceipt.h | 24 +++++++++--- 6 files changed, 73 insertions(+), 27 deletions(-) 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/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..4dc6e630b1f 100644 --- a/libethereum/TransactionReceipt.cpp +++ b/libethereum/TransactionReceipt.cpp @@ -29,37 +29,50 @@ 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) - { + + if (!r[0].isData()) + BOOST_THROW_EXCEPTION(InvalidTransactionReceiptFormat()); + if (r[0].size() == 32) m_stateRoot = (h256)r[0]; - gasUsedIndex = 1; + else if (r[0].size() == 1) + { + m_statusCode = (uint8_t)r[0]; + m_hasStatusCode = true; } - m_gasUsed = (u256)r[gasUsedIndex]; - m_bloom = (LogBloom)r[gasUsedIndex + 1]; - for (auto const& i : r[gasUsedIndex + 2]) + 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): +TransactionReceipt::TransactionReceipt(h256 const& _root, u256 const& _gasUsed, LogEntries const& _log): + m_hasStatusCode(false), m_stateRoot(_root), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) {} +TransactionReceipt::TransactionReceipt(uint8_t _status, u256 const& _gasUsed, LogEntries const& _log): + m_hasStatusCode(true), + m_statusCode(_status), + m_gasUsed(_gasUsed), + m_bloom(eth::bloom(_log)), + m_log(_log) +{} + void TransactionReceipt::streamRLP(RLPStream& _s) const { - if (m_stateRoot) - _s.appendList(4) << m_stateRoot; + _s.appendList(4); + if (m_hasStatusCode) + _s << m_statusCode; else - _s.appendList(3); - + _s << m_stateRoot; + _s << m_gasUsed << m_bloom; _s.appendList(m_log.size()); @@ -67,9 +80,26 @@ void TransactionReceipt::streamRLP(RLPStream& _s) const l.streamRLP(_s); } +uint8_t TransactionReceipt::statusCode() const +{ + if (!m_hasStatusCode) + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); + return m_statusCode; +} + +h256 const& TransactionReceipt::stateRoot() const +{ + if (m_hasStatusCode) + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); + return m_stateRoot; +} + 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..5b47709e4b9 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -33,16 +33,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 { return m_hasStatusCode; } + /// @returns the state root. + /// @throw TransactionReceiptVersionError when m_hasStatusCode is true. + h256 const& stateRoot() const; + /// @returns the status code. + /// @throw TransactionReceiptVersionError when m_hasStatusCode is false. + 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 +59,12 @@ class TransactionReceipt bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); } private: - h256 m_stateRoot; + bool m_hasStatusCode = false; + union + { + uint8_t m_statusCode = 0; + h256 m_stateRoot; + }; u256 m_gasUsed; LogBloom m_bloom; LogEntries m_log; From 643eaa6cc1ce41112b843c3abf2295747e3187cf Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Wed, 19 Jul 2017 15:14:18 +0200 Subject: [PATCH 2/4] Update tests --- test/jsontests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0ddd07a982a138edad7b2d66e11610f0a8e93519 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Wed, 19 Jul 2017 19:41:23 +0200 Subject: [PATCH 3/4] Use boost::variant --- libethereum/Block.cpp | 9 +++- libethereum/TransactionReceipt.cpp | 78 ++++++++++++++++++++++-------- libethereum/TransactionReceipt.h | 11 ++--- libweb3jsonrpc/JsonHelper.cpp | 9 +++- 4 files changed, 78 insertions(+), 29 deletions(-) diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 068cc899c53..03c56fd8432 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/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp index 4dc6e630b1f..f4adae302ee 100644 --- a/libethereum/TransactionReceipt.cpp +++ b/libethereum/TransactionReceipt.cpp @@ -22,6 +22,10 @@ #include "TransactionReceipt.h" #include +#include +#include + + using namespace std; using namespace dev; using namespace dev::eth; @@ -35,12 +39,9 @@ TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) if (!r[0].isData()) BOOST_THROW_EXCEPTION(InvalidTransactionReceiptFormat()); if (r[0].size() == 32) - m_stateRoot = (h256)r[0]; + m_statusCodeOrStateRoot = (h256)r[0]; else if (r[0].size() == 1) - { - m_statusCode = (uint8_t)r[0]; - m_hasStatusCode = true; - } + m_statusCodeOrStateRoot = (uint8_t)r[0]; m_gasUsed = (u256)r[1]; m_bloom = (LogBloom)r[2]; @@ -50,16 +51,14 @@ TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) } TransactionReceipt::TransactionReceipt(h256 const& _root, u256 const& _gasUsed, LogEntries const& _log): - m_hasStatusCode(false), - m_stateRoot(_root), + 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_hasStatusCode(true), - m_statusCode(_status), + m_statusCodeOrStateRoot(_status), m_gasUsed(_gasUsed), m_bloom(eth::bloom(_log)), m_log(_log) @@ -67,31 +66,70 @@ TransactionReceipt::TransactionReceipt(uint8_t _status, u256 const& _gasUsed, Lo void TransactionReceipt::streamRLP(RLPStream& _s) const { - _s.appendList(4); - if (m_hasStatusCode) - _s << m_statusCode; - else - _s << m_stateRoot; + struct appenderVisitor : boost::static_visitor + { + appenderVisitor(RLPStream& _s) : m_stream(_s) {} + RLPStream& m_stream; + void operator()(uint8_t _statusCode) const + { + m_stream << _statusCode; + } + void operator()(h256 _stateRoot) const + { + m_stream << _stateRoot; + } + }; + _s.appendList(4); + boost::apply_visitor(appenderVisitor(_s), m_statusCodeOrStateRoot); _s << m_gasUsed << m_bloom; - _s.appendList(m_log.size()); for (LogEntry const& l: m_log) l.streamRLP(_s); } +bool TransactionReceipt::hasStatusCode() const +{ + struct hasStatusCodeVisitor : boost::static_visitor + { + bool operator()(uint8_t) const + { + return true; + } + bool operator()(h256) const + { + return false; + } + }; + return boost::apply_visitor(hasStatusCodeVisitor(), m_statusCodeOrStateRoot); +} + uint8_t TransactionReceipt::statusCode() const { - if (!m_hasStatusCode) - BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); - return m_statusCode; + struct statusCodeVisitor : boost::static_visitor + { + uint8_t operator()(uint8_t _statusCode) const + { + return _statusCode; + } + uint8_t operator()(h256) const + { + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); + } + }; + return boost::apply_visitor(statusCodeVisitor(), m_statusCodeOrStateRoot); } h256 const& TransactionReceipt::stateRoot() const { - if (m_hasStatusCode) + try + { + return boost::get(m_statusCodeOrStateRoot); + } + catch(const boost::bad_get&) + { BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); - return m_stateRoot; + } } std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionReceipt const& _r) diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index 5b47709e4b9..b78fe4a05b6 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -26,6 +26,8 @@ #include #include +#include + namespace dev { @@ -43,7 +45,7 @@ class TransactionReceipt 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 { return m_hasStatusCode; } + bool hasStatusCode() const; /// @returns the state root. /// @throw TransactionReceiptVersionError when m_hasStatusCode is true. h256 const& stateRoot() const; @@ -59,12 +61,7 @@ class TransactionReceipt bytes rlp() const { RLPStream s; streamRLP(s); return s.out(); } private: - bool m_hasStatusCode = false; - union - { - uint8_t m_statusCode = 0; - 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..362417bff13 100644 --- a/libweb3jsonrpc/JsonHelper.cpp +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -185,7 +185,14 @@ Json::Value toJson(dev::eth::TransactionSkeleton const& _t) Json::Value toJson(dev::eth::TransactionReceipt const& _t) { Json::Value res; - res["stateRoot"] = toJS(_t.stateRoot()); + try + { + res["stateRoot"] = toJS(_t.stateRoot()); + } + catch (TransactionReceiptVersionError const&) + { + res["statusCode"] = toJS(_t.statusCode()); + } res["gasUsed"] = toJS(_t.gasUsed()); res["bloom"] = toJS(_t.bloom()); res["log"] = dev::toJson(_t.log()); From c9d14fc0b66d23f57d793f822ace9a0e3d2f43c4 Mon Sep 17 00:00:00 2001 From: Yoichi Hirai Date: Thu, 20 Jul 2017 14:09:11 +0200 Subject: [PATCH 4/4] read variants in a simpler way --- libethereum/Block.cpp | 2 +- libethereum/TransactionReceipt.cpp | 63 +++++++----------------------- libethereum/TransactionReceipt.h | 4 +- libweb3jsonrpc/JsonHelper.cpp | 10 ++--- 4 files changed, 21 insertions(+), 58 deletions(-) diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 03c56fd8432..dcbffb1fcb7 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -868,7 +868,7 @@ h256 Block::stateRootBeforeTx(unsigned _i) const { return (_i > 0 ? receipt(_i - 1).stateRoot() : m_previousBlock.stateRoot()); } - catch(TransactionReceiptVersionError const&) + catch (TransactionReceiptVersionError const&) { return {}; } diff --git a/libethereum/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp index f4adae302ee..4b7152a3be9 100644 --- a/libethereum/TransactionReceipt.cpp +++ b/libethereum/TransactionReceipt.cpp @@ -22,10 +22,8 @@ #include "TransactionReceipt.h" #include -#include #include - using namespace std; using namespace dev; using namespace dev::eth; @@ -38,10 +36,13 @@ TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) 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]; @@ -66,22 +67,11 @@ TransactionReceipt::TransactionReceipt(uint8_t _status, u256 const& _gasUsed, Lo void TransactionReceipt::streamRLP(RLPStream& _s) const { - struct appenderVisitor : boost::static_visitor - { - appenderVisitor(RLPStream& _s) : m_stream(_s) {} - RLPStream& m_stream; - void operator()(uint8_t _statusCode) const - { - m_stream << _statusCode; - } - void operator()(h256 _stateRoot) const - { - m_stream << _stateRoot; - } - }; - _s.appendList(4); - boost::apply_visitor(appenderVisitor(_s), m_statusCodeOrStateRoot); + if (hasStatusCode()) + _s << statusCode(); + else + _s << stateRoot(); _s << m_gasUsed << m_bloom; _s.appendList(m_log.size()); for (LogEntry const& l: m_log) @@ -90,46 +80,23 @@ void TransactionReceipt::streamRLP(RLPStream& _s) const bool TransactionReceipt::hasStatusCode() const { - struct hasStatusCodeVisitor : boost::static_visitor - { - bool operator()(uint8_t) const - { - return true; - } - bool operator()(h256) const - { - return false; - } - }; - return boost::apply_visitor(hasStatusCodeVisitor(), m_statusCodeOrStateRoot); + return m_statusCodeOrStateRoot.which() == 0; } uint8_t TransactionReceipt::statusCode() const { - struct statusCodeVisitor : boost::static_visitor - { - uint8_t operator()(uint8_t _statusCode) const - { - return _statusCode; - } - uint8_t operator()(h256) const - { - BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); - } - }; - return boost::apply_visitor(statusCodeVisitor(), m_statusCodeOrStateRoot); + if (hasStatusCode()) + return boost::get(m_statusCodeOrStateRoot); + else + BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); } h256 const& TransactionReceipt::stateRoot() const { - try - { - return boost::get(m_statusCodeOrStateRoot); - } - catch(const boost::bad_get&) - { + if (hasStatusCode()) BOOST_THROW_EXCEPTION(TransactionReceiptVersionError()); - } + else + return boost::get(m_statusCodeOrStateRoot); } std::ostream& dev::eth::operator<<(std::ostream& _out, TransactionReceipt const& _r) diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index b78fe4a05b6..0cc8d1b7c05 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -47,10 +47,10 @@ class TransactionReceipt /// @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 m_hasStatusCode is true. + /// @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 m_hasStatusCode is false. + /// @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; } diff --git a/libweb3jsonrpc/JsonHelper.cpp b/libweb3jsonrpc/JsonHelper.cpp index 362417bff13..895f1dd15f8 100644 --- a/libweb3jsonrpc/JsonHelper.cpp +++ b/libweb3jsonrpc/JsonHelper.cpp @@ -185,14 +185,10 @@ Json::Value toJson(dev::eth::TransactionSkeleton const& _t) Json::Value toJson(dev::eth::TransactionReceipt const& _t) { Json::Value res; - try - { - res["stateRoot"] = toJS(_t.stateRoot()); - } - catch (TransactionReceiptVersionError const&) - { + 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());