From fdbb63a668148b7a322bdb4a2808872ca43bf357 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 30 Aug 2018 17:57:15 +0200 Subject: [PATCH] Move m_storageOverlay and m_storageOriginal caches management into Account. --- libethereum/Account.cpp | 16 +++++++++++++ libethereum/Account.h | 47 +++++++++++++++++--------------------- libethereum/SecureTrieDB.h | 35 ++++++++++++++++++++++++++++ libethereum/State.cpp | 33 +++----------------------- libethereum/State.h | 47 +++++++++++++++++++------------------- 5 files changed, 99 insertions(+), 79 deletions(-) create mode 100644 libethereum/SecureTrieDB.h diff --git a/libethereum/Account.cpp b/libethereum/Account.cpp index 8d98cae820f..ba2b6af615d 100644 --- a/libethereum/Account.cpp +++ b/libethereum/Account.cpp @@ -20,8 +20,10 @@ */ #include "Account.h" +#include "SecureTrieDB.h" #include "ValidationSchemes.h" #include +#include #include #include @@ -39,6 +41,20 @@ void Account::setCode(bytes&& _code) m_codeHash = sha3(m_codeCache); } +u256 Account::originalStorageValue(u256 const& _key, OverlayDB const& _db) const +{ + auto it = m_storageOriginal.find(_key); + if (it != m_storageOriginal.end()) + return it->second; + + // Not in the original values cache - go to the DB. + SecureTrieDB const memdb(const_cast(&_db), m_storageRoot); + std::string const payload = memdb.at(_key); + auto const value = payload.size() ? RLP(payload).toInt() : 0; + m_storageOriginal[_key] = value; + return value; +} + namespace js = json_spirit; namespace diff --git a/libethereum/Account.h b/libethereum/Account.h index 04645d85392..3f6208c84a9 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -22,13 +22,16 @@ #pragma once #include -#include -#include #include +#include #include +#include + namespace dev { +class OverlayDB; + namespace eth { @@ -45,20 +48,6 @@ namespace eth * in the overlay, stored in this class and retrieved with storageOverlay(). setStorage allows the overlay * to be altered. * - * The code handling explicitly supports a two-stage commit model needed for contract-creation. When creating - * a contract (running the initialisation code), the code of the account is considered empty. The attribute - * of emptiness can be retrieved with codeBearing(). After initialisation one must set the code accordingly; - * the code of the Account can be set with setCode(). To validate a setCode() call, this class records the - * state of being in contract-creation (and thus in a state where setCode may validly be called). It can be - * determined through isFreshCode(). - * - * The code can be retrieved through code(), and its hash through codeHash(). codeHash() is only valid when - * the account is not in the contract-creation phase (i.e. when isFreshCode() returns false). This class - * supports populating code on-demand from the state database. To determine if the code has been prepopulated - * call codeCacheValid(). To populate the code, look it up with codeHash() and populate with noteCode(). - * - * @todo: need to make a noteCodeCommitted(). - * * The constructor allows you to create an one of a number of "types" of accounts. The default constructor * makes a dead account (this is ignored by State when writing out the Trie). Another three allow a basic * or contract account to be specified along with an initial balance. The fina two allow either a basic or @@ -132,16 +121,28 @@ class Account /// the account. void setNonce(u256 const& _nonce) { m_nonce = _nonce; changed(); } - /// @returns the root of the trie (whose nodes are stored in the state db externally to this class) /// which encodes the base-state of the account's storage (upon which the storage is overlaid). h256 baseRoot() const { assert(m_storageRoot); return m_storageRoot; } + /// @returns account's storage value corresponding to the @_key + /// taking into account overlayed modifications + u256 storageValue(u256 const& _key, OverlayDB const& _db) const + { + auto mit = m_storageOverlay.find(_key); + if (mit != m_storageOverlay.end()) + return mit->second; + + return originalStorageValue(_key, _db); + } + + /// @returns account's original storage value corresponding to the @_key + /// not taking into account overlayed modifications + u256 originalStorageValue(u256 const& _key, OverlayDB const& _db) const; + /// @returns the storage overlay as a simple hash map. std::unordered_map const& storageOverlay() const { return m_storageOverlay; } - std::unordered_map const& storageOriginal() const { return m_storageOriginal; } - /// Set a key/value pair in the account's storage. This actually goes into the overlay, for committing /// to the trie later. void setStorage(u256 _p, u256 _v) { m_storageOverlay[_p] = _v; changed(); } @@ -152,12 +153,6 @@ class Account /// Set the storage root. Used when clearStorage() is reverted. void setStorageRoot(h256 const& _root) { m_storageOverlay.clear(); m_storageRoot = _root; changed(); } - /// Set a key/value pair in the account's storage to a value that is already present inside the - /// database. - void setStorageCache(u256 _p, u256 _v) const { m_storageOverlay[_p] = _v; } - - void setStorageOriginal(u256 _p, u256 _v) const { m_storageOriginal[_p] = _v; } - /// @returns the hash of the account's code. h256 codeHash() const { return m_codeHash; } @@ -210,7 +205,7 @@ class Account /// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie. mutable std::unordered_map m_storageOverlay; - /// The original values of the modified storage slots stored in m_storageOverlay. + /// The cache of unmodifed storage items mutable std::unordered_map m_storageOriginal; /// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless diff --git a/libethereum/SecureTrieDB.h b/libethereum/SecureTrieDB.h new file mode 100644 index 00000000000..4bbb2db4aac --- /dev/null +++ b/libethereum/SecureTrieDB.h @@ -0,0 +1,35 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ + +#pragma once + +#include + +namespace dev +{ +namespace eth +{ +#if ETH_FATDB +template +using SecureTrieDB = SpecificTrieDB, KeyType>; +#else +template +using SecureTrieDB = SpecificTrieDB, KeyType>; +#endif + +} // namespace eth +} // namespace dev diff --git a/libethereum/State.cpp b/libethereum/State.cpp index c342fd602d1..b0f11bb5042 100644 --- a/libethereum/State.cpp +++ b/libethereum/State.cpp @@ -418,19 +418,7 @@ u256 State::getNonce(Address const& _addr) const u256 State::storage(Address const& _id, u256 const& _key) const { if (Account const* a = account(_id)) - { - auto mit = a->storageOverlay().find(_key); - if (mit != a->storageOverlay().end()) - return mit->second; - - // Not in the storage cache - go to the DB. - SecureTrieDB memdb(const_cast(&m_db), a->baseRoot()); // promise we won't change the overlay! :) - string payload = memdb.at(_key); - u256 ret = payload.size() ? RLP(payload).toInt() : 0; - a->setStorageCache(_key, ret); - a->setStorageOriginal(_key, ret); - return ret; - } + return a->storageValue(_key, m_db); else return 0; } @@ -438,28 +426,13 @@ u256 State::storage(Address const& _id, u256 const& _key) const void State::setStorage(Address const& _contract, u256 const& _key, u256 const& _value) { m_changeLog.emplace_back(_contract, _key, storage(_contract, _key)); - Account& a = m_cache[_contract]; - a.setStorage(_key, _value); - if (a.storageOriginal().find(_key) == a.storageOriginal().end()) - { - SecureTrieDB memdb(const_cast(&m_db), a.baseRoot()); - string payload = memdb.at(_key); - u256 original = payload.size() ? RLP(payload).toInt() : 0; - a.setStorageOriginal(_key, original); - } + m_cache[_contract].setStorage(_key, _value); } u256 State::originalStorageValue(Address const& _contract, u256 const& _key) const { if (Account const* a = account(_contract)) - { - auto it = a->storageOriginal().find(_key); - if (it != a->storageOriginal().end()) - return it->second; - - assert(a->storageOverlay().find(_key) == a->storageOverlay().end()); - return storage(_contract, _key); - } + return a->originalStorageValue(_key, m_db); else return 0; } diff --git a/libethereum/State.h b/libethereum/State.h index 8d33335c416..671a2af04fa 100644 --- a/libethereum/State.h +++ b/libethereum/State.h @@ -17,20 +17,20 @@ #pragma once -#include -#include +#include "Account.h" +#include "GasPricer.h" +#include "SecureTrieDB.h" +#include "Transaction.h" +#include "TransactionReceipt.h" #include -#include -#include #include -#include +#include #include +#include #include #include -#include "Account.h" -#include "Transaction.h" -#include "TransactionReceipt.h" -#include "GasPricer.h" +#include +#include namespace dev { @@ -73,15 +73,9 @@ enum class Permanence { Reverted, Committed, - Uncommitted ///< Uncommitted state for change log readings in tests. + Uncommitted ///< Uncommitted state for change log readings in tests. }; -#if ETH_FATDB -template using SecureTrieDB = SpecificTrieDB, KeyType>; -#else -template using SecureTrieDB = SpecificTrieDB, KeyType>; -#endif - DEV_SIMPLE_EXCEPTION(InvalidAccountStartNonceInState); DEV_SIMPLE_EXCEPTION(IncorrectAccountStartNonceInState); @@ -327,7 +321,7 @@ class State u256 const& requireAccountStartNonce() const; void noteAccountStartNonce(u256 const& _actual); - /// Create a savepoint in the state changelog. /// + /// Create a savepoint in the state changelog. /// @return The savepoint index that can be used in rollback() function. size_t savepoint() const; @@ -357,12 +351,19 @@ class State /// exception occurred. bool executeTransaction(Executive& _e, Transaction const& _t, OnOpFunc const& _onOp); - OverlayDB m_db; ///< Our overlay for the state tree. - SecureTrieDB m_state; ///< Our state tree, as an OverlayDB DB. - mutable std::unordered_map m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed. - mutable std::vector
m_unchangedCacheEntries; ///< Tracks entries in m_cache that can potentially be purged if it grows too large. - mutable std::set
m_nonExistingAccountsCache; ///< Tracks addresses that are known to not exist. - AddressHash m_touched; ///< Tracks all addresses touched so far. + /// Our overlay for the state tree. + OverlayDB m_db; + /// Our state tree, as an OverlayDB DB. + SecureTrieDB m_state; + /// Our address cache. This stores the states of each address that has (or at least might have) + /// been changed. + mutable std::unordered_map m_cache; + /// Tracks entries in m_cache that can potentially be purged if it grows too large. + mutable std::vector
m_unchangedCacheEntries; + /// Tracks addresses that are known to not exist. + mutable std::set
m_nonExistingAccountsCache; + /// Tracks all addresses touched so far. + AddressHash m_touched; u256 m_accountStartNonce;