aboutsummaryrefslogtreecommitdiff
path: root/core/state
diff options
context:
space:
mode:
Diffstat (limited to 'core/state')
-rw-r--r--core/state/database.go163
-rw-r--r--core/state/dump.go158
-rw-r--r--core/state/iterator.go154
-rw-r--r--core/state/journal.go245
-rw-r--r--core/state/state_object.go499
-rw-r--r--core/state/statedb.go799
-rw-r--r--core/state/sync.go42
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] {
+ continue
+ }
+ s.originStorage[key] = value
+
+ if (value == common.Hash{}) {
+ s.setError(tr.TryDelete(key[:]))
+ continue
+ }
+ // Encoding []byte cannot fail, ok to ignore the error.
+ v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
+ s.setError(tr.TryUpdate(key[:], v))
+ }
+ return tr
+}
+
+// UpdateRoot sets the trie root to the current root hash of
+func (s *stateObject)