diff options
Diffstat (limited to 'core/state')
-rw-r--r-- | core/state/database.go | 163 | ||||
-rw-r--r-- | core/state/dump.go | 158 | ||||
-rw-r--r-- | core/state/iterator.go | 154 | ||||
-rw-r--r-- | core/state/journal.go | 245 | ||||
-rw-r--r-- | core/state/state_object.go | 499 | ||||
-rw-r--r-- | core/state/statedb.go | 799 | ||||
-rw-r--r-- | core/state/sync.go | 42 |
7 files changed, 2060 insertions, 0 deletions
diff --git a/core/state/database.go b/core/state/database.go new file mode 100644 index 0000000..8c641c3 --- /dev/null +++ b/core/state/database.go @@ -0,0 +1,163 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "fmt" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/ethdb" + "github.com/ava-labs/go-ethereum/trie" + lru "github.com/hashicorp/golang-lru" +) + +const ( + // Number of codehash->size associations to keep. + codeSizeCacheSize = 100000 +) + +// Database wraps access to tries and contract code. +type Database interface { + // OpenTrie opens the main account trie. + OpenTrie(root common.Hash) (Trie, error) + + // OpenStorageTrie opens the storage trie of an account. + OpenStorageTrie(addrHash, root common.Hash) (Trie, error) + + // CopyTrie returns an independent copy of the given trie. + CopyTrie(Trie) Trie + + // ContractCode retrieves a particular contract's code. + ContractCode(addrHash, codeHash common.Hash) ([]byte, error) + + // ContractCodeSize retrieves a particular contracts code's size. + ContractCodeSize(addrHash, codeHash common.Hash) (int, error) + + // TrieDB retrieves the low level trie database used for data storage. + TrieDB() *trie.Database +} + +// Trie is a Ethereum Merkle Patricia trie. +type Trie interface { + // GetKey returns the sha3 preimage of a hashed key that was previously used + // to store a value. + // + // TODO(fjl): remove this when SecureTrie is removed + GetKey([]byte) []byte + + // TryGet returns the value for key stored in the trie. The value bytes must + // not be modified by the caller. If a node was not found in the database, a + // trie.MissingNodeError is returned. + TryGet(key []byte) ([]byte, error) + + // TryUpdate associates key with value in the trie. If value has length zero, any + // existing value is deleted from the trie. The value bytes must not be modified + // by the caller while they are stored in the trie. If a node was not found in the + // database, a trie.MissingNodeError is returned. + TryUpdate(key, value []byte) error + + // TryDelete removes any existing value for key from the trie. If a node was not + // found in the database, a trie.MissingNodeError is returned. + TryDelete(key []byte) error + + // Hash returns the root hash of the trie. It does not write to the database and + // can be used even if the trie doesn't have one. + Hash() common.Hash + + // Commit writes all nodes to the trie's memory database, tracking the internal + // and external (for account tries) references. + Commit(onleaf trie.LeafCallback) (common.Hash, error) + + // NodeIterator returns an iterator that returns nodes of the trie. Iteration + // starts at the key after the given start key. + NodeIterator(startKey []byte) trie.NodeIterator + + // Prove constructs a Merkle proof for key. The result contains all encoded nodes + // on the path to the value at key. The value itself is also included in the last + // node and can be retrieved by verifying the proof. + // + // If the trie does not contain a value for key, the returned proof contains all + // nodes of the longest existing prefix of the key (at least the root), ending + // with the node that proves the absence of the key. + Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error +} + +// NewDatabase creates a backing store for state. The returned database is safe for +// concurrent use, but does not retain any recent trie nodes in memory. To keep some +// historical state in memory, use the NewDatabaseWithCache constructor. +func NewDatabase(db ethdb.Database) Database { + return NewDatabaseWithCache(db, 0) +} + +// NewDatabaseWithCache creates a backing store for state. The returned database +// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a +// large memory cache. +func NewDatabaseWithCache(db ethdb.Database, cache int) Database { + csc, _ := lru.New(codeSizeCacheSize) + return &cachingDB{ + db: trie.NewDatabaseWithCache(db, cache), + codeSizeCache: csc, + } +} + +type cachingDB struct { + db *trie.Database + codeSizeCache *lru.Cache +} + +// OpenTrie opens the main account trie at a specific root hash. +func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { + return trie.NewSecure(root, db.db) +} + +// OpenStorageTrie opens the storage trie of an account. +func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { + return trie.NewSecure(root, db.db) +} + +// CopyTrie returns an independent copy of the given trie. +func (db *cachingDB) CopyTrie(t Trie) Trie { + switch t := t.(type) { + case *trie.SecureTrie: + return t.Copy() + default: + panic(fmt.Errorf("unknown trie type %T", t)) + } +} + +// ContractCode retrieves a particular contract's code. +func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { + code, err := db.db.Node(codeHash) + if err == nil { + db.codeSizeCache.Add(codeHash, len(code)) + } + return code, err +} + +// ContractCodeSize retrieves a particular contracts code's size. +func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { + if cached, ok := db.codeSizeCache.Get(codeHash); ok { + return cached.(int), nil + } + code, err := db.ContractCode(addrHash, codeHash) + return len(code), err +} + +// TrieDB retrieves any intermediate trie-node caching layer. +func (db *cachingDB) TrieDB() *trie.Database { + return db.db +} diff --git a/core/state/dump.go b/core/state/dump.go new file mode 100644 index 0000000..91c7d08 --- /dev/null +++ b/core/state/dump.go @@ -0,0 +1,158 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "encoding/json" + "fmt" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/log" + "github.com/ava-labs/go-ethereum/rlp" + "github.com/ava-labs/go-ethereum/trie" +) + +// DumpAccount represents an account in the state +type DumpAccount struct { + Balance string `json:"balance"` + Nonce uint64 `json:"nonce"` + Root string `json:"root"` + CodeHash string `json:"codeHash"` + Code string `json:"code,omitempty"` + Storage map[common.Hash]string `json:"storage,omitempty"` + Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode + SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key + +} + +// Dump represents the full dump in a collected format, as one large map +type Dump struct { + Root string `json:"root"` + Accounts map[common.Address]DumpAccount `json:"accounts"` +} + +// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively +type iterativeDump json.Encoder + +// Collector interface which the state trie calls during iteration +type collector interface { + onRoot(common.Hash) + onAccount(common.Address, DumpAccount) +} + +func (self *Dump) onRoot(root common.Hash) { + self.Root = fmt.Sprintf("%x", root) +} + +func (self *Dump) onAccount(addr common.Address, account DumpAccount) { + self.Accounts[addr] = account +} + +func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) { + dumpAccount := &DumpAccount{ + Balance: account.Balance, + Nonce: account.Nonce, + Root: account.Root, + CodeHash: account.CodeHash, + Code: account.Code, + Storage: account.Storage, + SecureKey: account.SecureKey, + Address: nil, + } + if addr != (common.Address{}) { + dumpAccount.Address = &addr + } + (*json.Encoder)(&self).Encode(dumpAccount) +} +func (self iterativeDump) onRoot(root common.Hash) { + (*json.Encoder)(&self).Encode(struct { + Root common.Hash `json:"root"` + }{root}) +} + +func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) { + emptyAddress := (common.Address{}) + missingPreimages := 0 + c.onRoot(self.trie.Hash()) + it := trie.NewIterator(self.trie.NodeIterator(nil)) + for it.Next() { + var data Account + if err := rlp.DecodeBytes(it.Value, &data); err != nil { + panic(err) + } + addr := common.BytesToAddress(self.trie.GetKey(it.Key)) + obj := newObject(nil, addr, data) + account := DumpAccount{ + Balance: data.Balance.String(), + Nonce: data.Nonce, + Root: common.Bytes2Hex(data.Root[:]), + CodeHash: common.Bytes2Hex(data.CodeHash), + } + if emptyAddress == addr { + // Preimage missing + missingPreimages++ + if excludeMissingPreimages { + continue + } + account.SecureKey = it.Key + } + if !excludeCode { + account.Code = common.Bytes2Hex(obj.Code(self.db)) + } + if !excludeStorage { + account.Storage = make(map[common.Hash]string) + storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil)) + for storageIt.Next() { + _, content, _, err := rlp.Split(storageIt.Value) + if err != nil { + log.Error("Failed to decode the value returned by iterator", "error", err) + continue + } + account.Storage[common.BytesToHash(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content) + } + } + c.onAccount(addr, account) + } + if missingPreimages > 0 { + log.Warn("Dump incomplete due to missing preimages", "missing", missingPreimages) + } +} + +// RawDump returns the entire state an a single large object +func (self *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump { + dump := &Dump{ + Accounts: make(map[common.Address]DumpAccount), + } + self.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages) + return *dump +} + +// Dump returns a JSON string representing the entire state as a single json-object +func (self *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte { + dump := self.RawDump(excludeCode, excludeStorage, excludeMissingPreimages) + json, err := json.MarshalIndent(dump, "", " ") + if err != nil { + fmt.Println("dump err", err) + } + return json +} + +// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout +func (self *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) { + self.dump(iterativeDump(*output), excludeCode, excludeStorage, excludeMissingPreimages) +} diff --git a/core/state/iterator.go b/core/state/iterator.go new file mode 100644 index 0000000..c6d2a48 --- /dev/null +++ b/core/state/iterator.go @@ -0,0 +1,154 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "bytes" + "fmt" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/rlp" + "github.com/ava-labs/go-ethereum/trie" +) + +// NodeIterator is an iterator to traverse the entire state trie post-order, +// including all of the contract code and contract state tries. +type NodeIterator struct { + state *StateDB // State being iterated + + stateIt trie.NodeIterator // Primary iterator for the global state trie + dataIt trie.NodeIterator // Secondary iterator for the data trie of a contract + + accountHash common.Hash // Hash of the node containing the account + codeHash common.Hash // Hash of the contract source code + code []byte // Source code associated with a contract + + Hash common.Hash // Hash of the current entry being iterated (nil if not standalone) + Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) + + Error error // Failure set in case of an internal error in the iterator +} + +// NewNodeIterator creates an post-order state node iterator. +func NewNodeIterator(state *StateDB) *NodeIterator { + return &NodeIterator{ + state: state, + } +} + +// Next moves the iterator to the next node, returning whether there are any +// further nodes. In case of an internal error this method returns false and +// sets the Error field to the encountered failure. +func (it *NodeIterator) Next() bool { + // If the iterator failed previously, don't do anything + if it.Error != nil { + return false + } + // Otherwise step forward with the iterator and report any errors + if err := it.step(); err != nil { + it.Error = err + return false + } + return it.retrieve() +} + +// step moves the iterator to the next entry of the state trie. +func (it *NodeIterator) step() error { + // Abort if we reached the end of the iteration + if it.state == nil { + return nil + } + // Initialize the iterator if we've just started + if it.stateIt == nil { + it.stateIt = it.state.trie.NodeIterator(nil) + } + // If we had data nodes previously, we surely have at least state nodes + if it.dataIt != nil { + if cont := it.dataIt.Next(true); !cont { + if it.dataIt.Error() != nil { + return it.dataIt.Error() + } + it.dataIt = nil + } + return nil + } + // If we had source code previously, discard that + if it.code != nil { + it.code = nil + return nil + } + // Step to the next state trie node, terminating if we're out of nodes + if cont := it.stateIt.Next(true); !cont { + if it.stateIt.Error() != nil { + return it.stateIt.Error() + } + it.state, it.stateIt = nil, nil + return nil + } + // If the state trie node is an internal entry, leave as is + if !it.stateIt.Leaf() { + return nil + } + // Otherwise we've reached an account node, initiate data iteration + var account Account + if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil { + return err + } + dataTrie, err := it.state.db.OpenStorageTrie(common.BytesToHash(it.stateIt.LeafKey()), account.Root) + if err != nil { + return err + } + it.dataIt = dataTrie.NodeIterator(nil) + if !it.dataIt.Next(true) { + it.dataIt = nil + } + if !bytes.Equal(account.CodeHash, emptyCodeHash) { + it.codeHash = common.BytesToHash(account.CodeHash) + addrHash := common.BytesToHash(it.stateIt.LeafKey()) + it.code, err = it.state.db.ContractCode(addrHash, common.BytesToHash(account.CodeHash)) + if err != nil { + return fmt.Errorf("code %x: %v", account.CodeHash, err) + } + } + it.accountHash = it.stateIt.Parent() + return nil +} + +// retrieve pulls and caches the current state entry the iterator is traversing. +// The method returns whether there are any more data left for inspection. +func (it *NodeIterator) retrieve() bool { + // Clear out any previously set values + it.Hash = common.Hash{} + + // If the iteration's done, return no available data + if it.state == nil { + return false + } + // Otherwise retrieve the current entry + switch { + case it.dataIt != nil: + it.Hash, it.Parent = it.dataIt.Hash(), it.dataIt.Parent() + if it.Parent == (common.Hash{}) { + it.Parent = it.accountHash + } + case it.code != nil: + it.Hash, it.Parent = it.codeHash, it.accountHash + case it.stateIt != nil: + it.Hash, it.Parent = it.stateIt.Hash(), it.stateIt.Parent() + } + return true +} diff --git a/core/state/journal.go b/core/state/journal.go new file mode 100644 index 0000000..6e85173 --- /dev/null +++ b/core/state/journal.go @@ -0,0 +1,245 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "math/big" + + "github.com/ava-labs/go-ethereum/common" +) + +// journalEntry is a modification entry in the state change journal that can be +// reverted on demand. +type journalEntry interface { + // revert undoes the changes introduced by this journal entry. + revert(*StateDB) + + // dirtied returns the Ethereum address modified by this journal entry. + dirtied() *common.Address +} + +// journal contains the list of state modifications applied since the last state +// commit. These are tracked to be able to be reverted in case of an execution +// exception or revertal request. +type journal struct { + entries []journalEntry // Current changes tracked by the journal + dirties map[common.Address]int // Dirty accounts and the number of changes +} + +// newJournal create a new initialized journal. +func newJournal() *journal { + return &journal{ + dirties: make(map[common.Address]int), + } +} + +// append inserts a new modification entry to the end of the change journal. +func (j *journal) append(entry journalEntry) { + j.entries = append(j.entries, entry) + if addr := entry.dirtied(); addr != nil { + j.dirties[*addr]++ + } +} + +// revert undoes a batch of journalled modifications along with any reverted +// dirty handling too. +func (j *journal) revert(statedb *StateDB, snapshot int) { + for i := len(j.entries) - 1; i >= snapshot; i-- { + // Undo the changes made by the operation + j.entries[i].revert(statedb) + + // Drop any dirty tracking induced by the change + if addr := j.entries[i].dirtied(); addr != nil { + if j.dirties[*addr]--; j.dirties[*addr] == 0 { + delete(j.dirties, *addr) + } + } + } + j.entries = j.entries[:snapshot] +} + +// dirty explicitly sets an address to dirty, even if the change entries would +// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD +// precompile consensus exception. +func (j *journal) dirty(addr common.Address) { + j.dirties[addr]++ +} + +// length returns the current number of entries in the journal. +func (j *journal) length() int { + return len(j.entries) +} + +type ( + // Changes to the account trie. + createObjectChange struct { + account *common.Address + } + resetObjectChange struct { + prev *stateObject + } + suicideChange struct { + account *common.Address + prev bool // whether account had already suicided + prevbalance *big.Int + } + + // Changes to individual accounts. + balanceChange struct { + account *common.Address + prev *big.Int + } + multiCoinEnable struct { + account *common.Address + } + nonceChange struct { + account *common.Address + prev uint64 + } + storageChange struct { + account *common.Address + key, prevalue common.Hash + } + codeChange struct { + account *common.Address + prevcode, prevhash []byte + } + + // Changes to other state values. + refundChange struct { + prev uint64 + } + addLogChange struct { + txhash common.Hash + } + addPreimageChange struct { + hash common.Hash + } + touchChange struct { + account *common.Address + prev bool + prevDirty bool + } +) + +func (ch createObjectChange) revert(s *StateDB) { + delete(s.stateObjects, *ch.account) + delete(s.stateObjectsDirty, *ch.account) +} + +func (ch createObjectChange) dirtied() *common.Address { + return ch.account +} + +func (ch resetObjectChange) revert(s *StateDB) { + s.setStateObject(ch.prev) +} + +func (ch resetObjectChange) dirtied() *common.Address { + return nil +} + +func (ch suicideChange) revert(s *StateDB) { + obj := s.getStateObject(*ch.account) + if obj != nil { + obj.suicided = ch.prev + obj.setBalance(ch.prevbalance) + } +} + +func (ch suicideChange) dirtied() *common.Address { + return ch.account +} + +var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") + +func (ch touchChange) revert(s *StateDB) { +} + +func (ch touchChange) dirtied() *common.Address { + return ch.account +} + +func (ch balanceChange) revert(s *StateDB) { + s.getStateObject(*ch.account).setBalance(ch.prev) +} + +func (ch balanceChange) dirtied() *common.Address { + return ch.account +} + +func (ch multiCoinEnable) revert(s *StateDB) { + s.getStateObject(*ch.account).data.IsMultiCoin = false +} + +func (ch multiCoinEnable) dirtied() *common.Address { + return ch.account +} + +func (ch nonceChange) revert(s *StateDB) { + s.getStateObject(*ch.account).setNonce(ch.prev) +} + +func (ch nonceChange) dirtied() *common.Address { + return ch.account +} + +func (ch codeChange) revert(s *StateDB) { + s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) +} + +func (ch codeChange) dirtied() *common.Address { + return ch.account +} + +func (ch storageChange) revert(s *StateDB) { + s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) +} + +func (ch storageChange) dirtied() *common.Address { + return ch.account +} + +func (ch refundChange) revert(s *StateDB) { + s.refund = ch.prev +} + +func (ch refundChange) dirtied() *common.Address { + return nil +} + +func (ch addLogChange) revert(s *StateDB) { + logs := s.logs[ch.txhash] + if len(logs) == 1 { + delete(s.logs, ch.txhash) + } else { + s.logs[ch.txhash] = logs[:len(logs)-1] + } + s.logSize-- +} + +func (ch addLogChange) dirtied() *common.Address { + return nil +} + +func (ch addPreimageChange) revert(s *StateDB) { + delete(s.preimages, ch.hash) +} + +func (ch addPreimageChange) dirtied() *common.Address { + return nil +} diff --git a/core/state/state_object.go b/core/state/state_object.go new file mode 100644 index 0000000..9c47dc4 --- /dev/null +++ b/core/state/state_object.go @@ -0,0 +1,499 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +package state + +import ( + "bytes" + "fmt" + "io" + "math/big" + "time" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/crypto" + "github.com/ava-labs/go-ethereum/metrics" + "github.com/ava-labs/go-ethereum/rlp" +) + +var emptyCodeHash = crypto.Keccak256(nil) + +type Code []byte + +func (c Code) String() string { + return string(c) //strings.Join(Disassemble(c), " ") +} + +type Storage map[common.Hash]common.Hash + +func (s Storage) String() (str string) { + for key, value := range s { + str += fmt.Sprintf("%X : %X\n", key, value) + } + + return +} + +func (s Storage) Copy() Storage { + cpy := make(Storage) + for key, value := range s { + cpy[key] = value + } + + return cpy +} + +// stateObject represents an Ethereum account which is being modified. +// +// The usage pattern is as follows: +// First you need to obtain a state object. +// Account values can be accessed and modified through the object. +// Finally, call CommitTrie to write the modified storage trie into a database. +type stateObject struct { + address common.Address + addrHash common.Hash // hash of ethereum address of the account + data Account + db *StateDB + + // DB error. + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + dbErr error + + // Write caches. + trie Trie // storage trie, which becomes non-nil on first access + code Code // contract bytecode, which gets set when code is loaded + + originStorage Storage // Storage cache of original entries to dedup rewrites + dirtyStorage Storage // Storage entries that need to be flushed to disk + fakeStorage Storage // Fake storage which constructed by caller for debugging purpose. + + // Cache flags. + // When an object is marked suicided it will be delete from the trie + // during the "update" phase of the state transition. + dirtyCode bool // true if the code was updated + suicided bool + deleted bool +} + +// empty returns whether the account is considered empty. +func (s *stateObject) empty() bool { + return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash) +} + +// Account is the Ethereum consensus representation of accounts. +// These objects are stored in the main account trie. +type Account struct { + Nonce uint64 + Balance *big.Int + Root common.Hash // merkle root of the storage trie + CodeHash []byte + IsMultiCoin bool +} + +// newObject creates a state object. +func newObject(db *StateDB, address common.Address, data Account) *stateObject { + if data.Balance == nil { + data.Balance = new(big.Int) + } + if data.CodeHash == nil { + data.CodeHash = emptyCodeHash + } + return &stateObject{ + db: db, + address: address, + addrHash: crypto.Keccak256Hash(address[:]), + data: data, + originStorage: make(Storage), + dirtyStorage: make(Storage), + } +} + +// EncodeRLP implements rlp.Encoder. +func (s *stateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, s.data) +} + +// setError remembers the first non-nil error it is called with. +func (s *stateObject) setError(err error) { + if s.dbErr == nil { + s.dbErr = err + } +} + +func (s *stateObject) markSuicided() { + s.suicided = true +} + +func (s *stateObject) touch() { + s.db.journal.append(touchChange{ + account: &s.address, + }) + if s.address == ripemd { + // Explicitly put it in the dirty-cache, which is otherwise generated from + // flattened journals. + s.db.journal.dirty(s.address) + } +} + +func (s *stateObject) getTrie(db Database) Trie { + if s.trie == nil { + var err error + s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root) + if err != nil { + s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{}) + s.setError(fmt.Errorf("can't create storage trie: %v", err)) + } + } + return s.trie +} + +// GetState retrieves a value from the account storage trie. +func (s *stateObject) GetState(db Database, key common.Hash) common.Hash { + // If the fake storage is set, only lookup the state here(in the debugging mode) + if s.fakeStorage != nil { + return s.fakeStorage[key] + } + // If we have a dirty value for this state entry, return it + value, dirty := s.dirtyStorage[key] + if dirty { + return value + } + // Otherwise return the entry's original value + return s.GetCommittedState(db, key) +} + +// GetCommittedState retrieves a value from the committed account storage trie. +func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash { + // If the fake storage is set, only lookup the state here(in the debugging mode) + if s.fakeStorage != nil { + return s.fakeStorage[key] + } + // If we have the original value cached, return that + value, cached := s.originStorage[key] + if cached { + return value + } + // Track the amount of time wasted on reading the storage trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now()) + } + // Otherwise load the value from the database + enc, err := s.getTrie(db).TryGet(key[:]) + if err != nil { + s.setError(err) + return common.Hash{} + } + if len(enc) > 0 { + _, content, _, err := rlp.Split(enc) + if err != nil { + s.setError(err) + } + value.SetBytes(content) + } + s.originStorage[key] = value + return value +} + +// SetState updates a value in account storage. +func (s *stateObject) SetState(db Database, key, value common.Hash) { + // If the fake storage is set, put the temporary state update here. + if s.fakeStorage != nil { + s.fakeStorage[key] = value + return + } + // If the new value is the same as old, don't set + prev := s.GetState(db, key) + if prev == value { + return + } + // New value is different, update and journal the change + s.db.journal.append(storageChange{ + account: &s.address, + key: key, + prevalue: prev, + }) + s.setState(key, value) +} + +// SetStorage replaces the entire state storage with the given one. +// +// After this function is called, all original state will be ignored and state +// lookup only happens in the fake state storage. +// +// Note this function should only be used for debugging purpose. +func (s *stateObject) SetStorage(storage map[common.Hash]common.Hash) { + // Allocate fake storage if it's nil. + if s.fakeStorage == nil { + s.fakeStorage = make(Storage) + } + for key, value := range storage { + s.fakeStorage[key] = value + } + // Don't bother journal since this function should only be used for + // debugging and the `fake` storage won't be committed to database. +} + +func (s *stateObject) setState(key, value common.Hash) { + s.dirtyStorage[key] = value +} + +// updateTrie writes cached storage modifications into the object's storage trie. +func (s *stateObject) updateTrie(db Database) Trie { + // Track the amount of time wasted on updating the storge trie + if metrics.EnabledExpensive { + defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) + } + // Update all the dirty slots in the trie + tr := s.getTrie(db) + for key, value := range s.dirtyStorage { + delete(s.dirtyStorage, key) + + // Skip noop changes, persist actual changes + if value == s.originStorage[key] { + cont |