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) 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..9c7535b
--- /dev/null
+++ b/core/state/statedb.go
@@ -0,0 +1,799 @@
+// 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")
+ zeroRoot = common.HexToHash("0000000000000000000000000000000000000000000000000000000000000000")
+
+ // 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 && stateObject.data.Root != zeroRoot {
+ return errors.New("not a fresh account")
+ }
+ if !stateObject.EnableMultiCoin() {
+ return errors.New("multi-coin mode already enabled")
+ }
+ return nil
+}
+
+func (self *StateDB) ForceEnableMultiCoin(addr common.Address) {
+ stateObject := self.GetOrNewStateObject(addr)
+ stateObject.EnableMultiCoin()
+}
+
+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
+}
'#n3151'>3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370