diff --git a/libethcore/Exceptions.h b/libethcore/Exceptions.h index 060731b8cb8..c36750764b1 100644 --- a/libethcore/Exceptions.h +++ b/libethcore/Exceptions.h @@ -75,6 +75,7 @@ DEV_SIMPLE_EXCEPTION(InvalidParentHash); DEV_SIMPLE_EXCEPTION(InvalidUncleParentHash); DEV_SIMPLE_EXCEPTION(InvalidNumber); DEV_SIMPLE_EXCEPTION(InvalidZeroSignatureTransaction); +DEV_SIMPLE_EXCEPTION(InvalidTransactionReceiptFormat); DEV_SIMPLE_EXCEPTION(BlockNotFound); DEV_SIMPLE_EXCEPTION(UnknownParent); diff --git a/libethereum/Block.cpp b/libethereum/Block.cpp index 40e73241a78..f385a778dbb 100644 --- a/libethereum/Block.cpp +++ b/libethereum/Block.cpp @@ -829,15 +829,10 @@ bool Block::sealBlock(bytesConstRef _header) return true; } -State Block::fromPending(unsigned _i) const +h256 Block::stateRootBeforeTx(unsigned _i) const { - State ret = m_state; _i = min(_i, m_transactions.size()); - if (!_i) - ret.setRoot(m_previousBlock.stateRoot()); - else - ret.setRoot(receipt(_i - 1).stateRoot()); - return ret; + return (_i > 0 ? receipt(_i - 1).stateRoot() : m_previousBlock.stateRoot()); } LogBloom Block::logBloom() const diff --git a/libethereum/Block.h b/libethereum/Block.h index 7853021b26d..dd1d17dcd14 100644 --- a/libethereum/Block.h +++ b/libethereum/Block.h @@ -202,10 +202,11 @@ class Block /// Get the bloom filter of a particular transaction that happened in the block. LogBloom const& logBloom(unsigned _i) const { return receipt(_i).bloom(); } - /// Get the State immediately after the given number of pending transactions have been applied. + /// Get the State root hash immediately after all previous transactions before transaction @a _i have been applied. /// If (_i == 0) returns the initial state of the block. /// If (_i == pending().size()) returns the final state of the block, prior to rewards. - State fromPending(unsigned _i) const; + /// Returns zero hash if intermediate state root is not available in the receipt (the case after EIP98) + h256 stateRootBeforeTx(unsigned _i) const; // State-change operations diff --git a/libethereum/Client.cpp b/libethereum/Client.cpp index ed61c5a63b4..ad87820a138 100644 --- a/libethereum/Client.cpp +++ b/libethereum/Client.cpp @@ -775,28 +775,6 @@ Block Client::block(h256 const& _blockHash, PopulationStatistics* o_stats) const } } -State Client::state(unsigned _txi, h256 const& _blockHash) const -{ - try - { - return block(_blockHash).fromPending(_txi); - } - catch (Exception& ex) - { - ex << errinfo_block(bc().block(_blockHash)); - onBadBlock(ex); - return State(chainParams().accountStartNonce); - } -} - -eth::State Client::state(unsigned _txi) const -{ - DEV_READ_GUARDED(x_postSeal) - return m_postSeal.fromPending(_txi); - assert(false); - return State(chainParams().accountStartNonce); -} - void Client::flushTransactions() { doWork(); diff --git a/libethereum/Client.h b/libethereum/Client.h index a8069d3211a..e8addb93301 100644 --- a/libethereum/Client.h +++ b/libethereum/Client.h @@ -112,12 +112,6 @@ class Client: public ClientBase, protected Worker // [PRIVATE API - only relevant for base clients, not available in general] /// Get the block. dev::eth::Block block(h256 const& _blockHash, PopulationStatistics* o_stats) const; - /// Get the state of the given block part way through execution, immediately before transaction - /// index @a _txi. - dev::eth::State state(unsigned _txi, h256 const& _block) const; - /// Get the state of the currently pending block part way through execution, immediately before - /// transaction index @a _txi. - dev::eth::State state(unsigned _txi) const; /// Get the object representing the current state of Ethereum. dev::eth::Block postState() const { ReadGuard l(x_postSeal); return m_postSeal; } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index fa37a4bce5b..9c06c2ebfc1 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -157,8 +157,8 @@ Executive::Executive(Block& _s, LastHashes const& _lh, unsigned _level): { } -Executive::Executive(State& _s, Block const& _block, unsigned _txIndex, BlockChain const& _bc, unsigned _level): - m_s(_s = _block.fromPending(_txIndex)), +Executive::Executive(State& io_s, Block const& _block, unsigned _txIndex, BlockChain const& _bc, unsigned _level): + m_s(createIntermediateState(io_s, _block, _txIndex, _bc)), m_envInfo(_block.info(), _bc.lastHashes(_block.info().parentHash()), _txIndex ? _block.receipt(_txIndex - 1).gasUsed() : 0), m_depth(_level), m_sealEngine(*_bc.sealEngine()) diff --git a/libethereum/Executive.h b/libethereum/Executive.h index 64c946d81a2..88f26ee60ef 100644 --- a/libethereum/Executive.h +++ b/libethereum/Executive.h @@ -122,7 +122,7 @@ class Executive * populating environment info from the given Block and the LastHashes portion from the BlockChain. * State is assigned the resultant value, but otherwise unused. */ - Executive(State& _s, Block const& _block, unsigned _txIndex, BlockChain const& _bc, unsigned _level = 0); + Executive(State& io_s, Block const& _block, unsigned _txIndex, BlockChain const& _bc, unsigned _level = 0); Executive(Executive const&) = delete; void operator=(Executive) = delete; diff --git a/libethereum/State.cpp b/libethereum/State.cpp index 05c49012272..a09ba748118 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -31,11 +31,11 @@ #include #include #include "BlockChain.h" +#include "Block.h" #include "CodeSizeCache.h" #include "Defaults.h" #include "ExtVM.h" #include "Executive.h" -#include "BlockChain.h" #include "TransactionQueue.h" using namespace std; @@ -49,6 +49,20 @@ const char* StateDetail::name() { return EthViolet "⚙" EthWhite " ◌"; } const char* StateTrace::name() { return EthViolet "⚙" EthGray " ◎"; } const char* StateChat::name() { return EthViolet "⚙" EthWhite " ◌"; } +namespace +{ + +void executeTransaction(Executive& _e, Transaction const& _t, OnOpFunc const& _onOp) +{ + _e.initialize(_t); + + if (!_e.execute()) + _e.go(_onOp); + _e.finalize(); +} + +} + State::State(u256 const& _accountStartNonce, OverlayDB const& _db, BaseState _bs): m_db(_db), m_state(&m_db), @@ -523,13 +537,9 @@ std::pair State::execute(EnvInfo const& _en Executive e(*this, _envInfo, _sealEngine); ExecutionResult res; e.setResultRecipient(res); - e.initialize(_t); - // OK - transaction looks valid - execute. - u256 startGasUsed = _envInfo.gasUsed(); - if (!e.execute()) - e.go(onOp); - e.finalize(); + u256 const startGasUsed = _envInfo.gasUsed(); + executeTransaction(e, _t, onOp); if (_p == Permanence::Reverted) m_cache.clear(); @@ -539,7 +549,24 @@ std::pair State::execute(EnvInfo const& _en commit(removeEmptyAccounts ? State::CommitBehaviour::RemoveEmptyAccounts : State::CommitBehaviour::KeepEmptyAccounts); } - return make_pair(res, TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs())); + TransactionReceipt const receipt = _envInfo.number() >= _sealEngine.chainParams().u256Param("metropolisForkBlock") ? + TransactionReceipt(startGasUsed + e.gasUsed(), e.logs()) : + TransactionReceipt(rootHash(), startGasUsed + e.gasUsed(), e.logs()); + return make_pair(res, receipt); +} + +void State::executeBlockTransactions(Block const& _block, unsigned _txCount, LastHashes const& _lastHashes, SealEngineFace const& _sealEngine) +{ + u256 gasUsed = 0; + for (unsigned i = 0; i < _txCount; ++i) + { + EnvInfo envInfo(_block.info(), _lastHashes, gasUsed); + + Executive e(*this, envInfo, _sealEngine); + executeTransaction(e, _block.pending()[i], OnOpFunc()); + + gasUsed += e.gasUsed(); + } } std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) @@ -617,3 +644,17 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s) } return _out; } + +State& dev::eth::createIntermediateState(State& o_s, Block const& _block, unsigned _txIndex, BlockChain const& _bc) +{ + o_s = _block.state(); + u256 const rootHash = _block.stateRootBeforeTx(_txIndex); + if (rootHash) + o_s.setRoot(rootHash); + else + { + o_s.setRoot(_block.stateRootBeforeTx(0)); + o_s.executeBlockTransactions(_block, _txIndex, _bc.lastHashes(_block.info().parentHash()), *_bc.sealEngine()); + } + return o_s; +} diff --git a/libethereum/State.h b/libethereum/State.h index 03464ad8add..0d438b94f45 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -95,7 +95,7 @@ DEV_SIMPLE_EXCEPTION(InvalidAccountStartNonceInState); DEV_SIMPLE_EXCEPTION(IncorrectAccountStartNonceInState); class SealEngineFace; - +class Executive; namespace detail { @@ -207,6 +207,10 @@ class State /// This will change the state accordingly. std::pair execute(EnvInfo const& _envInfo, SealEngineFace const& _sealEngine, Transaction const& _t, Permanence _p = Permanence::Committed, OnOpFunc const& _onOp = OnOpFunc()); + /// Execute @a _txCount transactions of a given block. + /// This will change the state accordingly. + void executeBlockTransactions(Block const& _block, unsigned _txCount, LastHashes const& _lastHashes, SealEngineFace const& _sealEngine); + /// Check if the address is in use. bool addressInUse(Address const& _address) const; @@ -337,6 +341,8 @@ class State std::ostream& operator<<(std::ostream& _out, State const& _s); +State& createIntermediateState(State& o_s, Block const& _block, unsigned _txIndex, BlockChain const& _bc); + template AddressHash commit(AccountMap const& _cache, SecureTrieDB& _state) { diff --git a/libethereum/TransactionReceipt.cpp b/libethereum/TransactionReceipt.cpp index eba991839e8..14e92f1a438 100644 --- a/libethereum/TransactionReceipt.cpp +++ b/libethereum/TransactionReceipt.cpp @@ -28,11 +28,21 @@ using namespace dev::eth; TransactionReceipt::TransactionReceipt(bytesConstRef _rlp) { RLP r(_rlp); - m_stateRoot = (h256)r[0]; - m_gasUsed = (u256)r[1]; - m_bloom = (LogBloom)r[2]; - for (auto const& i: r[3]) + if (!r.isList() || r.itemCount() < 3 || r.itemCount() > 4) + DEV_SIMPLE_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]) m_log.emplace_back(i); + } TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries const& _log): @@ -44,7 +54,13 @@ TransactionReceipt::TransactionReceipt(h256 _root, u256 _gasUsed, LogEntries con void TransactionReceipt::streamRLP(RLPStream& _s) const { - _s.appendList(4) << m_stateRoot << m_gasUsed << m_bloom; + if (m_stateRoot) + _s.appendList(4) << m_stateRoot; + else + _s.appendList(3); + + _s << m_gasUsed << m_bloom; + _s.appendList(m_log.size()); for (LogEntry const& l: m_log) l.streamRLP(_s); diff --git a/libethereum/TransactionReceipt.h b/libethereum/TransactionReceipt.h index 6043cc5ee66..61738c1053b 100644 --- a/libethereum/TransactionReceipt.h +++ b/libethereum/TransactionReceipt.h @@ -32,11 +32,15 @@ namespace dev 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) +/// 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; } u256 const& gasUsed() const { return m_gasUsed; } diff --git a/libweb3jsonrpc/Debug.cpp b/libweb3jsonrpc/Debug.cpp index 983431891e9..334b6d39969 100644 --- a/libweb3jsonrpc/Debug.cpp +++ b/libweb3jsonrpc/Debug.cpp @@ -61,13 +61,19 @@ Json::Value Debug::traceTransaction(Executive& _e, Transaction const& _t, Json:: Json::Value Debug::traceBlock(Block const& _block, Json::Value const& _json) { + State s(_block.state()); + s.setRoot(_block.stateRootBeforeTx(0)); + Json::Value traces(Json::arrayValue); for (unsigned k = 0; k < _block.pending().size(); k++) { Transaction t = _block.pending()[k]; - State s(State::Null); + + u256 const gasUsed = k ? _block.receipt(k - 1).gasUsed() : 0; + EnvInfo envInfo(_block.info(), m_eth.blockChain().lastHashes(_block.info().parentHash()), gasUsed); + Executive e(s, envInfo, *m_eth.blockChain().sealEngine()); + eth::ExecutionResult er; - Executive e(s, _block, k, m_eth.blockChain()); e.setResultRecipient(er); traces.append(traceTransaction(e, t, _json)); } @@ -136,7 +142,8 @@ Json::Value Debug::debug_storageRangeAt(string const& _blockHashOrNumber, int _t Block block = m_eth.block(blockHash(_blockHashOrNumber)); unsigned const i = ((unsigned)_txIndex < block.pending().size()) ? (unsigned)_txIndex : block.pending().size(); - State state = block.fromPending(i); + State state(State::Null); + createIntermediateState(state, block, i, m_eth.blockChain()); map> const storage(state.storage(Address(_address))); diff --git a/test/jsontests b/test/jsontests index c1375d55be2..82e7385f219 160000 --- a/test/jsontests +++ b/test/jsontests @@ -1 +1 @@ -Subproject commit c1375d55be21ff5b37a91a061cca0a2fe0e9b920 +Subproject commit 82e7385f219ba361fe98dac282fb33905ae3c1f7 diff --git a/test/tools/libtesteth/BlockChainHelper.cpp b/test/tools/libtesteth/BlockChainHelper.cpp index b7d61e9c863..7fbf1dafe87 100644 --- a/test/tools/libtesteth/BlockChainHelper.cpp +++ b/test/tools/libtesteth/BlockChainHelper.cpp @@ -501,8 +501,9 @@ bool TestBlockChain::addBlock(TestBlock const& _block) Block block = (m_blockChain.get()->genesisBlock(genesisDB)); block.sync(*m_blockChain.get()); - //BOOST_REQUIRE(m_lastBlock.blockHeader().hash() == BlockHeader(block.blockData()).hash()); - m_lastBlock.setState(block.fromPending(10000)); + State st(block.state()); + st.setRoot(block.info().stateRoot()); + m_lastBlock.setState(st); return true; } diff --git a/test/unittests/libethereum/Block.cpp b/test/unittests/libethereum/Block.cpp index d734646d628..8d894f3c3e9 100644 --- a/test/unittests/libethereum/Block.cpp +++ b/test/unittests/libethereum/Block.cpp @@ -50,8 +50,6 @@ BOOST_AUTO_TEST_CASE(bStructures) BOOST_AUTO_TEST_CASE(bStates) { - if (!dev::test::Options::get().quadratic) - return; try { TestBlockChain testBlockchain(TestBlockChain::defaultGenesisBlock()); @@ -59,7 +57,7 @@ BOOST_AUTO_TEST_CASE(bStates) OverlayDB const& genesisDB = genesisBlock.state().db(); BlockChain const& blockchain = testBlockchain.interface(); - State stateBofore = testBlockchain.topBlock().state(); + h256 stateRootBefore = testBlockchain.topBlock().state().rootHash(); TestBlock testBlock; TestTransaction transaction1 = TestTransaction::defaultTransaction(1); @@ -76,15 +74,15 @@ BOOST_AUTO_TEST_CASE(bStates) Block block2 = blockchain.genesisBlock(genesisDB); block2.populateFromChain(blockchain, testBlock.blockHeader().hash()); - State stateAfterInsert = block2.fromPending(0); //get the state of blockchain on previous block - BOOST_REQUIRE(ImportTest::compareStates(stateBofore, stateAfterInsert) == 0); + h256 stateRootAfterInsert = block2.stateRootBeforeTx(0); //get the state of blockchain on previous block + BOOST_REQUIRE_EQUAL(stateRootBefore, stateRootAfterInsert); - State stateAfterInsert1 = block2.fromPending(1); //get the state of blockchain on current block executed - BOOST_REQUIRE(ImportTest::compareStates(stateAfterInsert, stateAfterInsert1, eth::AccountMaskMap(), WhenError::DontThrow) == 1); + h256 stateRootAfterInsert1 = block2.stateRootBeforeTx(1); //get the state of blockchain on current block executed + BOOST_REQUIRE(stateRootAfterInsert != stateRootAfterInsert1); - State stateAfterInsert2 = block2.fromPending(2); //get the state of blockchain on current block executed - BOOST_REQUIRE(ImportTest::compareStates(stateBofore, stateAfterInsert2, eth::AccountMaskMap(), WhenError::DontThrow) == 1); - BOOST_REQUIRE(ImportTest::compareStates(stateAfterInsert1, stateAfterInsert2, eth::AccountMaskMap(), WhenError::DontThrow) == 1); + h256 stateRootAfterInsert2 = block2.stateRootBeforeTx(2); //get the state of blockchain on current block executed + BOOST_REQUIRE(stateRootBefore != stateRootAfterInsert2); + BOOST_REQUIRE(stateRootAfterInsert1 != stateRootAfterInsert2); //Block2 will start a new block on top of blockchain BOOST_REQUIRE(block1.info() == block2.info());