Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Implement net gas metering for SSTORE in LegacyVM #5231

Merged
merged 3 commits into from
Aug 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libethcore/EVMSchedule.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct EVMSchedule
bool haveDelegateCall = true;
bool eip150Mode = false;
bool eip158Mode = false;
bool eip1283Mode = false;
bool haveBitwiseShifting = false;
bool haveRevert = false;
bool haveReturnData = false;
Expand All @@ -47,7 +48,9 @@ struct EVMSchedule
unsigned sloadGas = 50;
unsigned sstoreSetGas = 20000;
unsigned sstoreResetGas = 5000;
unsigned sstoreUnchangedGas = 200;
unsigned sstoreRefundGas = 15000;
unsigned sstoreRefundNonzeroGas = 4800;
unsigned jumpdestGas = 1;
unsigned logGas = 375;
unsigned logDataGas = 8;
Expand Down Expand Up @@ -135,6 +138,7 @@ static const EVMSchedule ConstantinopleSchedule = []
schedule.haveCreate2 = true;
schedule.haveBitwiseShifting = true;
schedule.haveExtcodehash = true;
schedule.eip1283Mode = true;
return schedule;
}();

Expand Down
16 changes: 16 additions & 0 deletions libethereum/Account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
*/

#include "Account.h"
#include "SecureTrieDB.h"
#include "ValidationSchemes.h"
#include <libdevcore/JsonUtils.h>
#include <libdevcore/OverlayDB.h>
#include <libethcore/ChainOperationParams.h>
#include <libethcore/Precompiled.h>

Expand All @@ -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<h256, OverlayDB> const memdb(const_cast<OverlayDB*>(&_db), m_storageRoot);
std::string const payload = memdb.at(_key);
auto const value = payload.size() ? RLP(payload).toInt<u256>() : 0;
m_storageOriginal[_key] = value;
return value;
}

namespace js = json_spirit;

namespace
Expand Down
69 changes: 41 additions & 28 deletions libethereum/Account.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
#pragma once

#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/TrieCommon.h>
#include <libethcore/Common.h>

#include <boost/filesystem/path.hpp>

namespace dev
{
class OverlayDB;

namespace eth
{

Expand All @@ -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
Expand Down Expand Up @@ -88,8 +77,19 @@ class Account
Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash, Changedness _c): m_isAlive(true), m_isUnchanged(_c == Unchanged), m_nonce(_nonce), m_balance(_balance), m_storageRoot(_contractRoot), m_codeHash(_codeHash) { assert(_contractRoot); }


/// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns false.
void kill() { m_isAlive = false; m_storageOverlay.clear(); m_codeHash = EmptySHA3; m_storageRoot = EmptyTrie; m_balance = 0; m_nonce = 0; changed(); }
/// Kill this account. Useful for the suicide opcode. Following this call, isAlive() returns
/// false.
void kill()
{
m_isAlive = false;
m_storageOverlay.clear();
m_storageOriginal.clear();
m_codeHash = EmptySHA3;
m_storageRoot = EmptyTrie;
m_balance = 0;
m_nonce = 0;
changed();
}

/// @returns true iff this object represents an account in the state. Returns false if this object
/// represents an account that should no longer exist in the trie (an account that never existed or was
Expand Down Expand Up @@ -121,11 +121,25 @@ 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<u256, u256> const& storageOverlay() const { return m_storageOverlay; }

Expand All @@ -139,10 +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 { const_cast<decltype(m_storageOverlay)&>(m_storageOverlay)[_p] = _v; }

/// @returns the hash of the account's code.
h256 codeHash() const { return m_codeHash; }

Expand Down Expand Up @@ -193,10 +203,13 @@ class Account
h256 m_codeHash = EmptySHA3;

/// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie.
std::unordered_map<u256, u256> m_storageOverlay;
mutable std::unordered_map<u256, u256> m_storageOverlay;

/// The cache of unmodifed storage items
mutable std::unordered_map<u256, u256> m_storageOriginal;

/// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless m_codeHash
/// equals c_contractConceptionCodeHash.
/// The associated code for this account. The SHA3 of this should be equal to m_codeHash unless
/// m_codeHash equals c_contractConceptionCodeHash.
bytes m_codeCache;

/// Value for m_codeHash when this account is having its code determined.
Expand Down Expand Up @@ -249,7 +262,7 @@ class PrecompiledContract;
using PrecompiledContractMap = std::unordered_map<Address, PrecompiledContract>;

AccountMap jsonToAccountMap(std::string const& _json, u256 const& _defaultNonce = 0,
AccountMaskMap* o_mask = nullptr, PrecompiledContractMap* o_precompiled = nullptr,
const boost::filesystem::path& _configPath = {});
AccountMaskMap* o_mask = nullptr, PrecompiledContractMap* o_precompiled = nullptr,
const boost::filesystem::path& _configPath = {});
}
}
6 changes: 6 additions & 0 deletions libethereum/ExtVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ class ExtVM : public ExtVMFace
/// Write a value in storage.
void setStore(u256 _n, u256 _v) final;

/// Read original storage value (before modifications in the current transaction).
u256 originalStorageValue(u256 const& _key) final
{
return m_s.originalStorageValue(myAddress, _key);
}

/// Read address's code.
bytes const& codeAt(Address _a) final { return m_s.code(_a); }

Expand Down
35 changes: 35 additions & 0 deletions libethereum/SecureTrieDB.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#pragma once

#include <libdevcore/TrieDB.h>

namespace dev
{
namespace eth
{
#if ETH_FATDB
template <class KeyType, class DB>
using SecureTrieDB = SpecificTrieDB<FatGenericTrieDB<DB>, KeyType>;
#else
template <class KeyType, class DB>
using SecureTrieDB = SpecificTrieDB<HashedGenericTrieDB<DB>, KeyType>;
#endif

} // namespace eth
} // namespace dev
21 changes: 9 additions & 12 deletions libethereum/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +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<h256, OverlayDB> memdb(const_cast<OverlayDB*>(&m_db), a->baseRoot()); // promise we won't change the overlay! :)
string payload = memdb.at(_key);
u256 ret = payload.size() ? RLP(payload).toInt<u256>() : 0;
a->setStorageCache(_key, ret);
return ret;
}
return a->storageValue(_key, m_db);
else
return 0;
}
Expand All @@ -440,6 +429,14 @@ void State::setStorage(Address const& _contract, u256 const& _key, u256 const& _
m_cache[_contract].setStorage(_key, _value);
}

u256 State::originalStorageValue(Address const& _contract, u256 const& _key) const
{
if (Account const* a = account(_contract))
return a->originalStorageValue(_key, m_db);
else
return 0;
}

void State::clearStorage(Address const& _contract)
{
h256 const& oldHash{m_cache[_contract].baseRoot()};
Expand Down
52 changes: 29 additions & 23 deletions libethereum/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@

#pragma once

#include <array>
#include <unordered_map>
#include "Account.h"
#include "GasPricer.h"
#include "SecureTrieDB.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include <libdevcore/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/TrieDB.h>
#include <libdevcore/OverlayDB.h>
#include <libethcore/Exceptions.h>
#include <libdevcore/RLP.h>
#include <libethcore/BlockHeader.h>
#include <libethcore/Exceptions.h>
#include <libethereum/CodeSizeCache.h>
#include <libevm/ExtVMFace.h>
#include "Account.h"
#include "Transaction.h"
#include "TransactionReceipt.h"
#include "GasPricer.h"
#include <array>
#include <unordered_map>

namespace dev
{
Expand Down Expand Up @@ -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 <class KeyType, class DB> using SecureTrieDB = SpecificTrieDB<FatGenericTrieDB<DB>, KeyType>;
#else
template <class KeyType, class DB> using SecureTrieDB = SpecificTrieDB<HashedGenericTrieDB<DB>, KeyType>;
#endif

DEV_SIMPLE_EXCEPTION(InvalidAccountStartNonceInState);
DEV_SIMPLE_EXCEPTION(IncorrectAccountStartNonceInState);

Expand Down Expand Up @@ -266,6 +260,11 @@ class State
/// Set the value of a storage position of an account.
void setStorage(Address const& _contract, u256 const& _location, u256 const& _value);

/// Get the original value of a storage position of an account (before modifications saved in
/// account cache).
/// @returns 0 if no account exists at that address.
u256 originalStorageValue(Address const& _contract, u256 const& _key) const;

/// Clear the storage root hash of an account to the hash of the empty trie.
void clearStorage(Address const& _contract);

Expand Down Expand Up @@ -322,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;

Expand Down Expand Up @@ -352,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<Address, OverlayDB> m_state; ///< Our state tree, as an OverlayDB DB.
mutable std::unordered_map<Address, Account> m_cache; ///< Our address cache. This stores the states of each address that has (or at least might have) been changed.
mutable std::vector<Address> m_unchangedCacheEntries; ///< Tracks entries in m_cache that can potentially be purged if it grows too large.
mutable std::set<Address> 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<Address, OverlayDB> 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<Address, Account> m_cache;
/// Tracks entries in m_cache that can potentially be purged if it grows too large.
mutable std::vector<Address> m_unchangedCacheEntries;
/// Tracks addresses that are known to not exist.
mutable std::set<Address> m_nonExistingAccountsCache;
/// Tracks all addresses touched so far.
AddressHash m_touched;

u256 m_accountStartNonce;

Expand Down
3 changes: 3 additions & 0 deletions libevm/ExtVMFace.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ class ExtVMFace: public evmc_context
/// Write a value in storage.
virtual void setStore(u256, u256) {}

/// Read original storage value (before modifications in the current transaction).
virtual u256 originalStorageValue(u256 const&) { return 0; }

/// Read address's balance.
virtual u256 balance(Address) { return 0; }

Expand Down
Loading