aboutsummaryrefslogtreecommitdiff
path: root/core/state
diff options
context:
space:
mode:
authorDeterminant <[email protected]>2020-06-28 14:47:41 -0400
committerDeterminant <[email protected]>2020-06-28 14:47:41 -0400
commitd235e2c6a5788ec4a6cff15a16f56b38a3876a0d (patch)
tree5f2727f7a50ee5840f889c82776d3a30a88dd59b /core/state
parent13ebd8bd9468e9d769d598b0ca2afb72ba78cb97 (diff)
...
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.go793
-rw-r--r--core/state/sync.go42
7 files changed, 2054 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) updateRoot(db Database) {
+ s.updateTrie(db)
+
+ // Track the amount of time wasted on hashing the storge trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
+ }
+ s.data.Root = s.trie.Hash()
+}
+
+// CommitTrie the storage trie of the object to db.
+// This updates the trie root.
+func (s *stateObject) CommitTrie(db Database) error {
+ s.updateTrie(db)
+ if s.dbErr != nil {
+ return s.dbErr
+ }
+ // Track the amount of time wasted on committing the storge trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
+ }
+ root, err := s.trie.Commit(nil)
+ if err == nil {
+ s.data.Root = root
+ }
+ return err
+}
+
+// AddBalance removes amount from c's balance.
+// It is used to add funds to the destination account of a transfer.
+func (s *stateObject) AddBalance(amount *big.Int) {
+ // EIP158: We must check emptiness for the objects such that the account
+ // clearing (0,0,0 objects) can take effect.
+ if amount.Sign() == 0 {
+ if s.empty() {
+ s.touch()
+ }
+
+ return
+ }
+ s.SetBalance(new(big.Int).Add(s.Balance(), amount))
+}
+
+// SubBalance removes amount from c's balance.
+// It is used to remove funds from the origin account of a transfer.
+func (s *stateObject) SubBalance(amount *big.Int) {
+ if amount.Sign() == 0 {
+ return
+ }
+ s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
+}
+
+func (s *stateObject) SetBalance(amount *big.Int) {
+ s.db.journal.append(balanceChange{
+ account: &s.address,
+ prev: new(big.Int).Set(s.data.Balance),
+ })
+ s.setBalance(amount)
+}
+
+// AddBalance removes amount from c's balance.
+// It is used to add funds to the destination account of a transfer.
+func (s *stateObject) AddBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
+ if amount.Sign() == 0 {
+ if s.empty() {
+ s.touch()
+ }
+
+ return
+ }
+ s.SetBalanceMultiCoin(coinID, new(big.Int).Add(s.BalanceMultiCoin(coinID, db), amount), db)
+}
+
+// SubBalance removes amount from c's balance.
+// It is used to remove funds from the origin account of a transfer.
+func (s *stateObject) SubBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
+ if amount.Sign() == 0 {
+ return
+ }
+ s.SetBalanceMultiCoin(coinID, new(big.Int).Sub(s.BalanceMultiCoin(coinID, db), amount), db)
+}
+
+func (s *stateObject) SetBalanceMultiCoin(coinID common.Hash, amount *big.Int, db Database) {
+ NormalizeCoinID(&coinID)
+ s.SetState(db, coinID, common.BigToHash(amount))
+}
+
+func (s *stateObject) setBalance(amount *big.Int) {
+ s.data.Balance = amount
+}
+
+func (s *stateObject) enableMultiCoin() {
+ s.data.IsMultiCoin = true
+}
+
+// Return the gas back to the origin. Used by the Virtual machine or Closures
+func (s *stateObject) ReturnGas(gas *big.Int) {}
+
+func (s *stateObject) deepCopy(db *StateDB) *stateObject {
+ stateObject := newObject(db, s.address, s.data)
+ if s.trie != nil {
+ stateObject.trie = db.db.CopyTrie(s.trie)
+ }
+ stateObject.code = s.code
+ stateObject.dirtyStorage = s.dirtyStorage.Copy()
+ stateObject.originStorage = s.originStorage.Copy()
+ stateObject.suicided = s.suicided
+ stateObject.dirtyCode = s.dirtyCode
+ stateObject.deleted = s.deleted
+ return stateObject
+}
+
+//
+// Attribute accessors
+//
+
+// Returns the address of the contract/account
+func (s *stateObject) Address() common.Address {
+ return s.address
+}
+
+// Code returns the contract code associated with this object, if any.
+func (s *stateObject) Code(db Database) []byte {
+ if s.code != nil {
+ return s.code
+ }
+ if bytes.Equal(s.CodeHash(), emptyCodeHash) {
+ return nil
+ }
+ code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
+ if err != nil {
+ s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
+ }
+ s.code = code
+ return code
+}
+
+func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
+ prevcode := s.Code(s.db.db)
+ s.db.journal.append(codeChange{
+ account: &s.address,
+ prevhash: s.CodeHash(),
+ prevcode: prevcode,
+ })
+ s.setCode(codeHash, code)
+}
+
+func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
+ s.code = code
+ s.data.CodeHash = codeHash[:]
+ s.dirtyCode = true
+}
+
+func (s *stateObject) SetNonce(nonce uint64) {
+ s.db.journal.append(nonceChange{
+ account: &s.address,
+ prev: s.data.Nonce,
+ })
+ s.setNonce(nonce)
+}
+
+func (s *stateObject) setNonce(nonce uint64) {
+ s.data.Nonce = nonce
+}
+
+func (s *stateObject) CodeHash() []byte {
+ return s.data.CodeHash
+}
+
+func (s *stateObject) Balance() *big.Int {
+ return s.data.Balance
+}
+
+func IsMultiCoinKey(key common.Hash) bool {
+ return key[0]&0x01 == 0x01
+}
+
+func NormalizeCoinID(coinID *common.Hash) {
+ coinID[0] |= 0x01
+}
+
+func NormalizeStateKey(key *common.Hash) {
+ key[0] &= 0xfe
+}
+
+func (s *stateObject) BalanceMultiCoin(coinID common.Hash, db Database) *big.Int {
+ NormalizeCoinID(&coinID)
+ return s.GetState(db, coinID).Big()
+}
+
+func (s *stateObject) EnableMultiCoin() bool {
+ if s.data.IsMultiCoin {
+ return false
+ }
+ s.db.journal.append(multiCoinEnable{
+ account: &s.address,
+ })
+ s.enableMultiCoin()
+ return true
+}
+
+func (s *stateObject) IsMultiCoin() bool {
+ return s.data.IsMultiCoin
+}
+
+func (s *stateObject) Nonce() uint64 {
+ return s.data.Nonce
+}
+
+// Never called, but must be present to allow stateObject to be used
+// as a vm.Account interface that also satisfies the vm.ContractRef
+// interface. Interfaces are awesome.
+func (s *stateObject) Value() *big.Int {
+ panic("Value on stateObject should never be called")
+}
diff --git a/core/state/statedb.go b/core/state/statedb.go
new file mode 100644
index 0000000..12deebe
--- /dev/null
+++ b/core/state/statedb.go
@@ -0,0 +1,793 @@
+// 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 provides a caching layer atop the Ethereum state trie.
+package state
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "sort"
+ "time"
+
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ava-labs/go-ethereum/log"
+ "github.com/ava-labs/go-ethereum/metrics"
+ "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ava-labs/go-ethereum/trie"
+)
+
+type revision struct {
+ id int
+ journalIndex int
+}
+
+var (
+ // emptyRoot is the known root hash of an empty trie.
+ emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // emptyCode is the known hash of the empty EVM bytecode.
+ emptyCode = crypto.Keccak256Hash(nil)
+)
+
+type proofList [][]byte
+
+func (n *proofList) Put(key []byte, value []byte) error {
+ *n = append(*n, value)
+ return nil
+}
+
+func (n *proofList) Delete(key []byte) error {
+ panic("not supported")
+}
+
+// StateDBs within the ethereum protocol are used to store anything
+// within the merkle trie. StateDBs take care of caching and storing
+// nested states. It's the general query interface to retrieve:
+// * Contracts
+// * Accounts
+type StateDB struct {
+ db Database
+ trie Trie
+
+ // This map holds 'live' objects, which will get modified while processing a state transition.
+ stateObjects map[common.Address]*stateObject
+ stateObjectsDirty map[common.Address]struct{}
+
+ // 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
+
+ // The refund counter, also used by state transitioning.
+ refund uint64
+
+ thash, bhash common.Hash
+ txIndex int
+ logs map[common.Hash][]*types.Log
+ logSize uint
+
+ preimages map[common.Hash][]byte
+
+ // Journal of state modifications. This is the backbone of
+ // Snapshot and RevertToSnapshot.
+ journal *journal
+ validRevisions []revision
+ nextRevisionId int
+
+ // Measurements gathered during execution for debugging purposes
+ AccountReads time.Duration
+ AccountHashes time.Duration
+ AccountUpdates time.Duration
+ AccountCommits time.Duration
+ StorageReads time.Duration
+ StorageHashes time.Duration
+ StorageUpdates time.Duration
+ StorageCommits time.Duration
+}
+
+// Create a new state from a given trie.
+func New(root common.Hash, db Database) (*StateDB, error) {
+ tr, err := db.OpenTrie(root)
+ if err != nil {
+ return nil, err
+ }
+ return &StateDB{
+ db: db,
+ trie: tr,
+ stateObjects: make(map[common.Address]*stateObject),
+ stateObjectsDirty: make(map[common.Address]struct{}),
+ logs: make(map[common.Hash][]*types.Log),
+ preimages: make(map[common.Hash][]byte),
+ journal: newJournal(),
+ }, nil
+}
+
+// setError remembers the first non-nil error it is called with.
+func (self *StateDB) setError(err error) {
+ if self.dbErr == nil {
+ self.dbErr = err
+ }
+}
+
+func (self *StateDB) Error() error {
+ return self.dbErr
+}
+
+// Reset clears out all ephemeral state objects from the state db, but keeps
+// the underlying state trie to avoid reloading data for the next operations.
+func (self *StateDB) Reset(root common.Hash) error {
+ tr, err := self.db.OpenTrie(root)
+ if err != nil {
+ return err
+ }
+ self.trie = tr
+ self.stateObjects = make(map[common.Address]*stateObject)
+ self.stateObjectsDirty = make(map[common.Address]struct{})
+ self.thash = common.Hash{}
+ self.bhash = common.Hash{}
+ self.txIndex = 0
+ self.logs = make(map[common.Hash][]*types.Log)
+ self.logSize = 0
+ self.preimages = make(map[common.Hash][]byte)
+ self.clearJournalAndRefund()
+ return nil
+}
+
+func (self *StateDB) AddLog(log *types.Log) {
+ self.journal.append(addLogChange{txhash: self.thash})
+
+ log.TxHash = self.thash
+ log.BlockHash = self.bhash
+ log.TxIndex = uint(self.txIndex)
+ log.Index = self.logSize
+ self.logs[self.thash] = append(self.logs[self.thash], log)
+ self.logSize++
+}
+
+func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
+ return self.logs[hash]
+}
+
+func (self *StateDB) Logs() []*types.Log {
+ var logs []*types.Log
+ for _, lgs := range self.logs {
+ logs = append(logs, lgs...)
+ }
+ return logs
+}
+
+// AddPreimage records a SHA3 preimage seen by the VM.
+func (self *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
+ if _, ok := self.preimages[hash]; !ok {
+ self.journal.append(addPreimageChange{hash: hash})
+ pi := make([]byte, len(preimage))
+ copy(pi, preimage)
+ self.preimages[hash] = pi
+ }
+}
+
+// Preimages returns a list of SHA3 preimages that have been submitted.
+func (self *StateDB) Preimages() map[common.Hash][]byte {
+ return self.preimages
+}
+
+// AddRefund adds gas to the refund counter
+func (self *StateDB) AddRefund(gas uint64) {
+ self.journal.append(refundChange{prev: self.refund})
+ self.refund += gas
+}
+
+// SubRefund removes gas from the refund counter.
+// This method will panic if the refund counter goes below zero
+func (self *StateDB) SubRefund(gas uint64) {
+ self.journal.append(refundChange{prev: self.refund})
+ if gas > self.refund {
+ panic("Refund counter below zero")
+ }
+ self.refund -= gas
+}
+
+// Exist reports whether the given account address exists in the state.
+// Notably this also returns true for suicided accounts.
+func (self *StateDB) Exist(addr common.Address) bool {
+ return self.getStateObject(addr) != nil
+}
+
+// Empty returns whether the state object is either non-existent
+// or empty according to the EIP161 specification (balance = nonce = code = 0)
+func (self *StateDB) Empty(addr common.Address) bool {
+ so := self.getStateObject(addr)
+ return so == nil || so.empty()
+}
+
+// Retrieve the balance from the given address or 0 if object not found
+func (self *StateDB) GetBalance(addr common.Address) *big.Int {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Balance()
+ }
+ return common.Big0
+}
+
+// Retrieve the balance from the given address or 0 if object not found
+func (self *StateDB) GetBalanceMultiCoin(addr common.Address, coinID common.Hash) *big.Int {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.BalanceMultiCoin(coinID, self.db)
+ }
+ return common.Big0
+}
+
+func (self *StateDB) EnableMultiCoin(addr common.Address) error {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject.data.Root != emptyRoot {
+ return errors.New("not a fresh account")
+ }
+ if !stateObject.EnableMultiCoin() {
+ return errors.New("multi-coin mode already enabled")
+ }
+ return nil
+}
+
+func (self *StateDB) IsMultiCoin(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.IsMultiCoin()
+ }
+ return false
+}
+
+func (self *StateDB) GetNonce(addr common.Address) uint64 {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Nonce()
+ }
+
+ return 0
+}
+
+// TxIndex returns the current transaction index set by Prepare.
+func (self *StateDB) TxIndex() int {
+ return self.txIndex
+}
+
+// BlockHash returns the current block hash set by Prepare.
+func (self *StateDB) BlockHash() common.Hash {
+ return self.bhash
+}
+
+func (self *StateDB) GetCode(addr common.Address) []byte {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.Code(self.db)
+ }
+ return nil
+}
+
+func (self *StateDB) GetCodeSize(addr common.Address) int {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return 0
+ }
+ if stateObject.code != nil {
+ return len(stateObject.code)
+ }
+ size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+ if err != nil {
+ self.setError(err)
+ }
+ return size
+}
+
+func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return common.Hash{}
+ }
+ return common.BytesToHash(stateObject.CodeHash())
+}
+
+// GetState retrieves a value from the given account's storage trie.
+func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.GetState(self.db, hash)
+ }
+ return common.Hash{}
+}
+
+// GetProof returns the MerkleProof for a given Account
+func (self *StateDB) GetProof(a common.Address) ([][]byte, error) {
+ var proof proofList
+ err := self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
+ return [][]byte(proof), err
+}
+
+// GetProof returns the StorageProof for given key
+func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
+ var proof proofList
+ trie := self.StorageTrie(a)
+ if trie == nil {
+ return proof, errors.New("storage trie for requested address does not exist")
+ }
+ err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
+ return [][]byte(proof), err
+}
+
+// GetCommittedState retrieves a value from the given account's committed storage trie.
+func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.GetCommittedState(self.db, hash)
+ }
+ return common.Hash{}
+}
+
+// Database retrieves the low level database supporting the lower level trie ops.
+func (self *StateDB) Database() Database {
+ return self.db
+}
+
+// StorageTrie returns the storage trie of an account.
+// The return value is a copy and is nil for non-existent accounts.
+func (self *StateDB) StorageTrie(addr common.Address) Trie {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return nil
+ }
+ cpy := stateObject.deepCopy(self)
+ return cpy.updateTrie(self.db)
+}
+
+func (self *StateDB) HasSuicided(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject != nil {
+ return stateObject.suicided
+ }
+ return false
+}
+
+/*
+ * SETTERS
+ */
+
+// AddBalance adds amount to the account associated with addr.
+func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.AddBalance(amount)
+ }
+}
+
+// SubBalance subtracts amount from the account associated with addr.
+func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalance(amount)
+ }
+}
+
+func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetBalance(amount)
+ }
+}
+
+// AddBalance adds amount to the account associated with addr.
+func (self *StateDB) AddBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.AddBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+// SubBalance subtracts amount from the account associated with addr.
+func (self *StateDB) SubBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SubBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+func (self *StateDB) SetBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetBalanceMultiCoin(coinID, amount, self.db)
+ }
+}
+
+func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetNonce(nonce)
+ }
+}
+
+func (self *StateDB) SetCode(addr common.Address, code []byte) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetCode(crypto.Keccak256Hash(code), code)
+ }
+}
+
+func (self *StateDB) SetState(addr common.Address, key, value common.Hash) (res error) {
+ res = nil
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ if stateObject.data.IsMultiCoin {
+ NormalizeStateKey(&key)
+ }
+ stateObject.SetState(self.db, key, value)
+ }
+ return
+}
+
+// SetStorage replaces the entire storage for the specified account with given
+// storage. This function should only be used for debugging.
+func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) {
+ stateObject := self.GetOrNewStateObject(addr)
+ if stateObject != nil {
+ stateObject.SetStorage(storage)
+ }
+}
+
+// Suicide marks the given account as suicided.
+// This clears the account balance.
+//
+// The account's state object is still available until the state is committed,
+// getStateObject will return a non-nil account after Suicide.
+func (self *StateDB) Suicide(addr common.Address) bool {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil {
+ return false
+ }
+ self.journal.append(suicideChange{
+ account: &addr,
+ prev: stateObject.suicided,
+ prevbalance: new(big.Int).Set(stateObject.Balance()),
+ })
+ stateObject.markSuicided()
+ stateObject.data.Balance = new(big.Int)
+
+ return true
+}
+
+//
+// Setting, updating & deleting state object methods.
+//
+
+// updateStateObject writes the given object to the trie.
+func (s *StateDB) updateStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on updating the account from the trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+ }
+ // Encode the account and update the account trie
+ addr := stateObject.Address()
+
+ data, err := rlp.EncodeToBytes(stateObject)
+ if err != nil {
+ panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
+ }
+ s.setError(s.trie.TryUpdate(addr[:], data))
+}
+
+// deleteStateObject removes the given object from the state trie.
+func (s *StateDB) deleteStateObject(stateObject *stateObject) {
+ // Track the amount of time wasted on deleting the account from the trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now())
+ }
+ // Delete the account from the trie
+ stateObject.deleted = true
+
+ addr := stateObject.Address()
+ s.setError(s.trie.TryDelete(addr[:]))
+}
+
+// Retrieve a state object given by the address. Returns nil if not found.
+func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
+ // Prefer live objects
+ if obj := s.stateObjects[addr]; obj != nil {
+ if obj.deleted {
+ return nil
+ }
+ return obj
+ }
+ // Track the amount of time wasted on loading the object from the database
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
+ }
+ // Load the object from the database
+ enc, err := s.trie.TryGet(addr[:])
+ if len(enc) == 0 {
+ s.setError(err)
+ return nil
+ }
+ var data Account
+ if err := rlp.DecodeBytes(enc, &data); err != nil {
+ log.Error("Failed to decode state object", "addr", addr, "err", err)
+ return nil
+ }
+ // Insert into the live set
+ obj := newObject(s, addr, data)
+ s.setStateObject(obj)
+ return obj
+}
+
+func (self *StateDB) setStateObject(object *stateObject) {
+ self.stateObjects[object.Address()] = object
+}
+
+// Retrieve a state object or create a new state object if nil.
+func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
+ stateObject := self.getStateObject(addr)
+ if stateObject == nil || stateObject.deleted {
+ stateObject, _ = self.createObject(addr)
+ }
+ return stateObject
+}
+
+// createObject creates a new state object. If there is an existing account with
+// the given address, it is overwritten and returned as the second return value.
+func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
+ prev = self.getStateObject(addr)
+ newobj = newObject(self, addr, Account{})
+ newobj.setNonce(0) // sets the object to dirty
+ if prev == nil {
+ self.journal.append(createObjectChange{account: &addr})
+ } else {
+ self.journal.append(resetObjectChange{prev: prev})
+ }
+ self.setStateObject(newobj)
+ return newobj, prev
+}
+
+// CreateAccount explicitly creates a state object. If a state object with the address
+// already exists the balance is carried over to the new account.
+//
+// CreateAccount is called during the EVM CREATE operation. The situation might arise that
+// a contract does the following:
+//
+// 1. sends funds to sha(account ++ (nonce + 1))
+// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
+//
+// Carrying over the balance ensures that Ether doesn't disappear.
+func (self *StateDB) CreateAccount(addr common.Address) {
+ newObj, prev := self.createObject(addr)
+ if prev != nil {
+ newObj.setBalance(prev.data.Balance)
+ }
+}
+
+func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
+ so := db.getStateObject(addr)
+ if so == nil {
+ return nil
+ }
+ it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
+
+ for it.Next() {
+ key := common.BytesToHash(db.trie.GetKey(it.Key))
+ if value, dirty := so.dirtyStorage[key]; dirty {
+ if !cb(key, value) {
+ return nil
+ }
+ continue
+ }
+
+ if len(it.Value) > 0 {
+ _, content, _, err := rlp.Split(it.Value)
+ if err != nil {
+ return err
+ }
+ if !cb(key, common.BytesToHash(content)) {
+ return nil
+ }
+ }
+ }
+ return nil
+}
+
+// Copy creates a deep, independent copy of the state.
+// Snapshots of the copied state cannot be applied to the copy.
+func (self *StateDB) Copy() *StateDB {
+ // Copy all the basic fields, initialize the memory ones
+ state := &StateDB{
+ db: self.db,
+ trie: self.db.CopyTrie(self.trie),
+ stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)),
+ stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)),
+ refund: self.refund,
+ logs: make(map[common.Hash][]*types.Log, len(self.logs)),
+ logSize: self.logSize,
+ preimages: make(map[common.Hash][]byte, len(self.preimages)),
+ journal: newJournal(),
+ }
+ // Copy the dirty states, logs, and preimages
+ for addr := range self.journal.dirties {
+ // As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
+ // and in the Finalise-method, there is a case where an object is in the journal but not
+ // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
+ // nil
+ if object, exist := self.stateObjects[addr]; exist {
+ state.stateObjects[addr] = object.deepCopy(state)
+ state.stateObjectsDirty[addr] = struct{}{}
+ }
+ }
+ // Above, we don't copy the actual journal. This means that if the copy is copied, the
+ // loop above will be a no-op, since the copy's journal is empty.
+ // Thus, here we iterate over stateObjects, to enable copies of copies
+ for addr := range self.stateObjectsDirty {
+ if _, exist := state.stateObjects[addr]; !exist {
+ state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state)
+ state.stateObjectsDirty[addr] = struct{}{}
+ }
+ }
+ for hash, logs := range self.logs {
+ cpy := make([]*types.Log, len(logs))
+ for i, l := range logs {
+ cpy[i] = new(types.Log)
+ *cpy[i] = *l
+ }
+ state.logs[hash] = cpy
+ }
+ for hash, preimage := range self.preimages {
+ state.preimages[hash] = preimage
+ }
+ return state
+}
+
+// Snapshot returns an identifier for the current revision of the state.
+func (self *StateDB) Snapshot() int {
+ id := self.nextRevisionId
+ self.nextRevisionId++
+ self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
+ return id
+}
+
+// RevertToSnapshot reverts all state changes made since the given revision.
+func (self *StateDB) RevertToSnapshot(revid int) {
+ // Find the snapshot in the stack of valid snapshots.
+ idx := sort.Search(len(self.validRevisions), func(i int) bool {
+ return self.validRevisions[i].id >= revid
+ })
+ if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
+ panic(fmt.Errorf("revision id %v cannot be reverted", revid))
+ }
+ snapshot := self.validRevisions[idx].journalIndex
+
+ // Replay the journal to undo changes and remove invalidated snapshots
+ self.journal.revert(self, snapshot)
+ self.validRevisions = self.validRevisions[:idx]
+}
+
+// GetRefund returns the current value of the refund counter.
+func (self *StateDB) GetRefund() uint64 {
+ return self.refund
+}
+
+// Finalise finalises the state by removing the self destructed objects
+// and clears the journal as well as the refunds.
+func (s *StateDB) Finalise(deleteEmptyObjects bool) {
+ for addr := range s.journal.dirties {
+ stateObject, exist := s.stateObjects[addr]
+ if !exist {
+ // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
+ // That tx goes out of gas, and although the notion of 'touched' does not exist there, the
+ // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
+ // it will persist in the journal even though the journal is reverted. In this special circumstance,
+ // it may exist in `s.journal.dirties` but not in `s.stateObjects`.
+ // Thus, we can safely ignore it here
+ continue
+ }
+
+ if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
+ s.deleteStateObject(stateObject)
+ } else {
+ stateObject.updateRoot(s.db)
+ s.updateStateObject(stateObject)
+ }
+ s.stateObjectsDirty[addr] = struct{}{}
+ }
+ // Invalidate journal because reverting across transactions is not allowed.
+ s.clearJournalAndRefund()
+}
+
+// IntermediateRoot computes the current root hash of the state trie.
+// It is called in between transactions to get the root hash that
+// goes into transaction receipts.
+func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
+ s.Finalise(deleteEmptyObjects)
+
+ // Track the amount of time wasted on hashing the account trie
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now())
+ }
+ return s.trie.Hash()
+}
+
+// Prepare sets the current transaction hash and index and block hash which is
+// used when the EVM emits new state logs.
+func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
+ self.thash = thash
+ self.bhash = bhash
+ self.txIndex = ti
+}
+
+func (s *StateDB) clearJournalAndRefund() {
+ s.journal = newJournal()
+ s.validRevisions = s.validRevisions[:0]
+ s.refund = 0
+}
+
+// Commit writes the state to the underlying in-memory trie database.
+func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
+ defer s.clearJournalAndRefund()
+
+ for addr := range s.journal.dirties {
+ s.stateObjectsDirty[addr] = struct{}{}
+ }
+ // Commit objects to the trie, measuring the elapsed time
+ for addr, stateObject := range s.stateObjects {
+ _, isDirty := s.stateObjectsDirty[addr]
+ switch {
+ case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
+ // If the object has been removed, don't bother syncing it
+ // and just mark it for deletion in the trie.
+ s.deleteStateObject(stateObject)
+ case isDirty:
+ // Write any contract code associated with the state object
+ if stateObject.code != nil && stateObject.dirtyCode {
+ s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
+ stateObject.dirtyCode = false
+ }
+ // Write any storage changes in the state object to its storage trie.
+ if err := stateObject.CommitTrie(s.db); err != nil {
+ return common.Hash{}, err
+ }
+ // Update the object in the main account trie.
+ s.updateStateObject(stateObject)
+ }
+ delete(s.stateObjectsDirty, addr)
+ }
+ // Write the account trie changes, measuing the amount of wasted time
+ if metrics.EnabledExpensive {
+ defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now())
+ }
+ root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
+ var account Account
+ if err := rlp.DecodeBytes(leaf, &account); err != nil {
+ return nil
+ }
+ if account.Root != emptyRoot {
+ s.db.TrieDB().Reference(account.Root, parent)
+ }
+ code := common.BytesToHash(account.CodeHash)
+ if code != emptyCode {
+ s.db.TrieDB().Reference(code, parent)
+ }
+ return nil
+ })
+ return root, err
+}
diff --git a/core/state/sync.go b/core/state/sync.go
new file mode 100644
index 0000000..873395c
--- /dev/null
+++ b/core/state/sync.go
@@ -0,0 +1,42 @@
+// 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"
+
+ "github.com/ava-labs/go-ethereum/common"
+ "github.com/ava-labs/go-ethereum/ethdb"
+ "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ava-labs/go-ethereum/trie"
+)
+
+// NewStateSync create a new state trie download scheduler.
+func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom) *trie.Sync {
+ var syncer *trie.Sync
+ callback := func(leaf []byte, parent common.Hash) error {
+ var obj Account
+ if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
+ return err
+ }
+ syncer.AddSubTrie(obj.Root, 64, parent, nil)
+ syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
+ return nil
+ }
+ syncer = trie.NewSync(root, database, callback, bloom)
+ return syncer
+}