aboutsummaryrefslogtreecommitdiffstats
path: root/core/state/statedb.go
diff options
context:
space:
mode:
authorFelix Lange <fjl@users.noreply.github.com>2017-06-27 21:57:06 +0800
committerGitHub <noreply@github.com>2017-06-27 21:57:06 +0800
commit9e5f03b6c487175cc5aa1224e5e12fd573f483a7 (patch)
tree475e573ff6c7e77cd069a2f6238afdb27d4bce43 /core/state/statedb.go
parentbb366271fe33cf87b462dc5a25ac6c448ac6d2e1 (diff)
downloadgo-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar.gz
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar.bz2
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar.lz
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar.xz
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.tar.zst
go-tangerine-9e5f03b6c487175cc5aa1224e5e12fd573f483a7.zip
core/state: access trie through Database interface, track errors (#14589)
With this commit, core/state's access to the underlying key/value database is mediated through an interface. Database errors are tracked in StateDB and returned by CommitTo or the new Error method. Motivation for this change: We can remove the light client's duplicated copy of core/state. The light client now supports node iteration, so tracing and storage enumeration can work with the light client (not implemented in this commit).
Diffstat (limited to 'core/state/statedb.go')
-rw-r--r--core/state/statedb.go139
1 files changed, 32 insertions, 107 deletions
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 05869a0c8..694374f82 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -26,23 +26,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
- lru "github.com/hashicorp/golang-lru"
-)
-
-// Trie cache generation limit after which to evic trie nodes from memory.
-var MaxTrieCacheGen = uint16(120)
-
-const (
- // Number of past tries to keep. This value is chosen such that
- // reasonable chain reorg depths will hit an existing trie.
- maxPastTries = 12
-
- // Number of codehash->size associations to keep.
- codeSizeCacheSize = 100000
)
type revision struct {
@@ -56,16 +42,21 @@ type revision struct {
// * Contracts
// * Accounts
type StateDB struct {
- db ethdb.Database
- trie *trie.SecureTrie
- pastTries []*trie.SecureTrie
- codeSizeCache *lru.Cache
+ 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{}
stateObjectsDestructed 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 *big.Int
@@ -86,16 +77,14 @@ type StateDB struct {
}
// Create a new state from a given trie
-func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
- tr, err := trie.NewSecure(root, db, MaxTrieCacheGen)
+func New(root common.Hash, db Database) (*StateDB, error) {
+ tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
- csc, _ := lru.New(codeSizeCacheSize)
return &StateDB{
db: db,
trie: tr,
- codeSizeCache: csc,
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
stateObjectsDestructed: make(map[common.Address]struct{}),
@@ -105,36 +94,21 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
}, nil
}
-// New creates a new statedb by reusing any journalled tries to avoid costly
-// disk io.
-func (self *StateDB) New(root common.Hash) (*StateDB, error) {
- self.lock.Lock()
- defer self.lock.Unlock()
-
- tr, err := self.openTrie(root)
- if err != nil {
- return nil, err
+// setError remembers the first non-nil error it is called with.
+func (self *StateDB) setError(err error) {
+ if self.dbErr == nil {
+ self.dbErr = err
}
- return &StateDB{
- db: self.db,
- trie: tr,
- codeSizeCache: self.codeSizeCache,
- stateObjects: make(map[common.Address]*stateObject),
- stateObjectsDirty: make(map[common.Address]struct{}),
- stateObjectsDestructed: make(map[common.Address]struct{}),
- refund: new(big.Int),
- logs: make(map[common.Hash][]*types.Log),
- preimages: make(map[common.Hash][]byte),
- }, nil
+}
+
+func (self *StateDB) Error() error {
+ return self.dbErr
}
// Reset clears out all emphemeral 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 {
- self.lock.Lock()
- defer self.lock.Unlock()
-
- tr, err := self.openTrie(root)
+ tr, err := self.db.OpenTrie(root)
if err != nil {
return err
}
@@ -149,34 +123,9 @@ func (self *StateDB) Reset(root common.Hash) error {
self.logSize = 0
self.preimages = make(map[common.Hash][]byte)
self.clearJournalAndRefund()
-
return nil
}
-// openTrie creates a trie. It uses an existing trie if one is available
-// from the journal if available.
-func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
- for i := len(self.pastTries) - 1; i >= 0; i-- {
- if self.pastTries[i].Hash() == root {
- tr := *self.pastTries[i]
- return &tr, nil
- }
- }
- return trie.NewSecure(root, self.db, MaxTrieCacheGen)
-}
-
-func (self *StateDB) pushTrie(t *trie.SecureTrie) {
- self.lock.Lock()
- defer self.lock.Unlock()
-
- if len(self.pastTries) >= maxPastTries {
- copy(self.pastTries, self.pastTries[1:])
- self.pastTries[len(self.pastTries)-1] = t
- } else {
- self.pastTries = append(self.pastTries, t)
- }
-}
-
func (self *StateDB) AddLog(log *types.Log) {
self.journal = append(self.journal, addLogChange{txhash: self.thash})
@@ -254,10 +203,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.getStateObject(addr)
if stateObject != nil {
- code := stateObject.Code(self.db)
- key := common.BytesToHash(stateObject.CodeHash())
- self.codeSizeCache.Add(key, len(code))
- return code
+ return stateObject.Code(self.db)
}
return nil
}
@@ -267,13 +213,12 @@ func (self *StateDB) GetCodeSize(addr common.Address) int {
if stateObject == nil {
return 0
}
- key := common.BytesToHash(stateObject.CodeHash())
- if cached, ok := self.codeSizeCache.Get(key); ok {
- return cached.(int)
+ if stateObject.code != nil {
+ return len(stateObject.code)
}
- size := len(stateObject.Code(self.db))
- if stateObject.dbErr == nil {
- self.codeSizeCache.Add(key, size)
+ size, err := self.db.ContractCodeSize(stateObject.addrHash, common.BytesToHash(stateObject.CodeHash()))
+ if err != nil {
+ self.setError(err)
}
return size
}
@@ -296,7 +241,7 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
// 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(a common.Address) *trie.SecureTrie {
+func (self *StateDB) StorageTrie(a common.Address) Trie {
stateObject := self.getStateObject(a)
if stateObject == nil {
return nil
@@ -394,14 +339,14 @@ func (self *StateDB) updateStateObject(stateObject *stateObject) {
if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
}
- self.trie.Update(addr[:], data)
+ self.setError(self.trie.TryUpdate(addr[:], data))
}
// deleteStateObject removes the given object from the state trie.
func (self *StateDB) deleteStateObject(stateObject *stateObject) {
stateObject.deleted = true
addr := stateObject.Address()
- self.trie.Delete(addr[:])
+ self.setError(self.trie.TryDelete(addr[:]))
}
// Retrieve a state object given my the address. Returns nil if not found.
@@ -415,8 +360,9 @@ func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObje
}
// Load the object from the database.
- enc := self.trie.Get(addr[:])
+ enc, err := self.trie.TryGet(addr[:])
if len(enc) == 0 {
+ self.setError(err)
return nil
}
var data Account
@@ -512,8 +458,6 @@ func (self *StateDB) Copy() *StateDB {
state := &StateDB{
db: self.db,
trie: self.trie,
- pastTries: self.pastTries,
- codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*stateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
stateObjectsDestructed: make(map[common.Address]struct{}, len(self.stateObjectsDestructed)),
@@ -636,23 +580,6 @@ func (s *StateDB) DeleteSuicides() {
}
}
-// Commit commits all state changes to the database.
-func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
- root, batch := s.CommitBatch(deleteEmptyObjects)
- return root, batch.Write()
-}
-
-// CommitBatch commits all state changes to a write batch but does not
-// execute the batch. It is used to validate state changes against
-// the root hash stored in a block.
-func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) {
- batch = s.db.NewBatch()
- root, _ = s.CommitTo(batch, deleteEmptyObjects)
-
- log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
- return root, batch
-}
-
func (s *StateDB) clearJournalAndRefund() {
s.journal = nil
s.validRevisions = s.validRevisions[:0]
@@ -690,8 +617,6 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
}
// Write trie changes.
root, err = s.trie.CommitTo(dbw)
- if err == nil {
- s.pushTrie(s.trie)
- }
+ log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
}