From 8b54fc0db0e4795c6e989f4416b6c5385c04f539 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 30 Oct 2014 14:38:19 +0100 Subject: [PATCH] Full documentation for Account class. --- libethereum/Account.cpp | 2 +- libethereum/Account.h | 112 +++++++++++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 13 deletions(-) diff --git a/libethereum/Account.cpp b/libethereum/Account.cpp index dcd97f037..74b6ebe7b 100644 --- a/libethereum/Account.cpp +++ b/libethereum/Account.cpp @@ -26,5 +26,5 @@ using namespace dev; using namespace dev::eth; #pragma GCC diagnostic ignored "-Wunused-variable" -namespace { char dummy; }; +const h256 Account::c_contractConceptionCodeHash; diff --git a/libethereum/Account.h b/libethereum/Account.h index 00344a9e6..154bb90d0 100644 --- a/libethereum/Account.h +++ b/libethereum/Account.h @@ -31,62 +31,150 @@ namespace dev namespace eth { -// TODO: Document fully. - +/** + * Models the state of a single Ethereum account. + * Used to cache a portion of the full Ethereum state. State keeps a mapping of Address's to Accounts. + * + * Aside from storing the nonce and balance, the account may also be "dead" (where isAlive() returns false). + * This allows State to explicitly store the notion of a deleted account in it's cache. kill() can be used + * for this. + * + * For the account's storage, the class operates a cache. baseRoot() specifies the base state of the storage + * given as the Trie root to be looked up in the state database. Alterations beyond this base are specified + * 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 + * a contract account to be created with arbitrary values. + * + * Code does + */ class Account { public: - enum NewAccountType { NormalCreation, ContractConception }; + /// Type of account to create. + enum NewAccountType + { + NormalCreation, ///< Normal account. + ContractConception ///< Contract account - we place this object into the contract-creation state (and as such we expect setCode(), but codeHash() won't work). + }; /// Construct a dead Account. Account() {} + /// Construct an alive Account, with given endowment, for either a normal (non-contract) account or for a contract account in the /// conception phase, where the code is not yet known. - Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? h256() : EmptySHA3) {} + Account(u256 _balance, NewAccountType _t): m_isAlive(true), m_balance(_balance), m_codeHash(_t == NormalCreation ? c_contractConceptionCodeHash : EmptySHA3) {} /// Explicit constructor for wierd cases of construction of a normal account. Account(u256 _nonce, u256 _balance): m_isAlive(true), m_nonce(_nonce), m_balance(_balance) {} + /// Explicit constructor for wierd cases of construction or a contract account. Account(u256 _nonce, u256 _balance, h256 _contractRoot, h256 _codeHash): m_isAlive(true), 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; } + + /// @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 suicided). bool isAlive() const { return m_isAlive; } + + /// @returns the balance of this account. Can be altered in place. u256& balance() { return m_balance; } + + /// @returns the balance of this account. u256 const& balance() const { return m_balance; } + + /// Increments the balance of this account by the given amount. It's a bigint, so can be negative. void addBalance(bigint _i) { m_balance = (u256)((bigint)m_balance + _i); } + /// @returns the nonce of the account. Can be altered in place. u256& nonce() { return m_nonce; } + + /// @returns the nonce of the account. u256 const& nonce() const { return m_nonce; } + + /// Increment the nonce of the account by one. void incNonce() { m_nonce++; } + + /// @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; } - std::map const& storage() const { return m_storageOverlay; } + + /// @returns the storage overlay as a simple map. + std::map const& storageOverlay() const { return m_storageOverlay; } + + /// 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; } - bool isFreshCode() const { return !m_codeHash; } + + /// @returns true if we are in the contract-conception state and setCode is valid to call. + bool isFreshCode() const { return m_codeHash == c_contractConceptionCodeHash; } + + /// @returns true if we are either in the contract-conception state or if the account's code is not empty. bool codeBearing() const { return m_codeHash != EmptySHA3; } - bool codeCacheValid() const { return m_codeHash == EmptySHA3 || !m_codeHash || m_codeCache.size(); } - h256 codeHash() const { assert(m_codeHash); return m_codeHash; } - bytes const& code() const { assert(m_codeHash == EmptySHA3 || !m_codeHash || m_codeCache.size()); return m_codeCache; } - void setCode(bytesConstRef _code) { assert(!m_codeHash); m_codeCache = _code.toBytes(); } + + /// @returns the hash of the account's code. Must only be called when isFreshCode() returns false. + h256 codeHash() const { assert(!isFreshCode()); return m_codeHash; } + + /// Sets the code of the account. Must only be called when isFreshCode() returns true. + void setCode(bytesConstRef _code) { assert(isFreshCode()); m_codeCache = _code.toBytes(); } + + /// @returns true if the account's code is available through code(). + bool codeCacheValid() const { return m_codeHash == EmptySHA3 || m_codeHash == c_contractConceptionCodeHash || m_codeCache.size(); } + + /// Specify to the object what the actual code is for the account. @a _code must have a SHA3 equal to codeHash() and + /// must only be called when isFreshCode() returns false. void noteCode(bytesConstRef _code) { assert(sha3(_code) == m_codeHash); m_codeCache = _code.toBytes(); } + /// @returns the account's code. Must only be called when codeCacheValid returns true. + bytes const& code() const { assert(codeCacheValid()); return m_codeCache; } + private: + /// Is this account existant? If not, it represents a deleted account. bool m_isAlive = false; + + /// Account's nonce. u256 m_nonce = 0; + + /// Account's balance. u256 m_balance = 0; /// The base storage root. Used with the state DB to give a base to the storage. m_storageOverlay is overlaid on this and takes precedence for all values set. h256 m_storageRoot = EmptyTrie; - /// If 0 then we're in the limbo where we're running the initialisation code. We expect a setCode() at some point later. + /// If c_contractConceptionCodeHash then we're in the limbo where we're running the initialisation code. We expect a setCode() at some point later. /// If EmptySHA3, then m_code, which should be empty, is valid. /// If anything else, then m_code is valid iff it's not empty, otherwise, State::ensureCached() needs to be called with the correct args. h256 m_codeHash = EmptySHA3; - // TODO: change to unordered_map. + /// The map with is overlaid onto whatever storage is implied by the m_storageRoot in the trie. std::map m_storageOverlay; + + /// 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. + static const h256 c_contractConceptionCodeHash; }; }