|
|
@@ -43,8 +43,14 @@ type StateDB struct {
|
|
|
db ethdb.Database
|
|
|
trie *trie.SecureTrie
|
|
|
|
|
|
- stateObjects map[string]*StateObject
|
|
|
+ // This map caches canon state accounts.
|
|
|
+ all map[common.Address]Account
|
|
|
|
|
|
+ // This map holds 'live' objects, which will get modified while processing a state transition.
|
|
|
+ stateObjects map[common.Address]*StateObject
|
|
|
+ stateObjectsDirty map[common.Address]struct{}
|
|
|
+
|
|
|
+ // The refund counter, also used by state transitioning.
|
|
|
refund *big.Int
|
|
|
|
|
|
thash, bhash common.Hash
|
|
|
@@ -60,32 +66,36 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
return &StateDB{
|
|
|
- db: db,
|
|
|
- trie: tr,
|
|
|
- stateObjects: make(map[string]*StateObject),
|
|
|
- refund: new(big.Int),
|
|
|
- logs: make(map[common.Hash]vm.Logs),
|
|
|
+ db: db,
|
|
|
+ trie: tr,
|
|
|
+ all: make(map[common.Address]Account),
|
|
|
+ stateObjects: make(map[common.Address]*StateObject),
|
|
|
+ stateObjectsDirty: make(map[common.Address]struct{}),
|
|
|
+ refund: new(big.Int),
|
|
|
+ logs: make(map[common.Hash]vm.Logs),
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
// 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 {
|
|
|
- var (
|
|
|
- err error
|
|
|
- tr = self.trie
|
|
|
- )
|
|
|
+ tr, err := trie.NewSecure(root, self.db)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ all := self.all
|
|
|
if self.trie.Hash() != root {
|
|
|
- if tr, err = trie.NewSecure(root, self.db); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
+ // The root has changed, invalidate canon state.
|
|
|
+ all = make(map[common.Address]Account)
|
|
|
}
|
|
|
*self = StateDB{
|
|
|
- db: self.db,
|
|
|
- trie: tr,
|
|
|
- stateObjects: make(map[string]*StateObject),
|
|
|
- refund: new(big.Int),
|
|
|
- logs: make(map[common.Hash]vm.Logs),
|
|
|
+ db: self.db,
|
|
|
+ trie: tr,
|
|
|
+ all: all,
|
|
|
+ stateObjects: make(map[common.Address]*StateObject),
|
|
|
+ stateObjectsDirty: make(map[common.Address]struct{}),
|
|
|
+ refund: new(big.Int),
|
|
|
+ logs: make(map[common.Hash]vm.Logs),
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
@@ -137,7 +147,7 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account {
|
|
|
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
|
|
stateObject := self.GetStateObject(addr)
|
|
|
if stateObject != nil {
|
|
|
- return stateObject.balance
|
|
|
+ return stateObject.Balance()
|
|
|
}
|
|
|
|
|
|
return common.Big0
|
|
|
@@ -146,7 +156,7 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
|
|
func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
|
|
stateObject := self.GetStateObject(addr)
|
|
|
if stateObject != nil {
|
|
|
- return stateObject.nonce
|
|
|
+ return stateObject.Nonce()
|
|
|
}
|
|
|
|
|
|
return StartingNonce
|
|
|
@@ -155,18 +165,24 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
|
|
func (self *StateDB) GetCode(addr common.Address) []byte {
|
|
|
stateObject := self.GetStateObject(addr)
|
|
|
if stateObject != nil {
|
|
|
- return stateObject.code
|
|
|
+ return stateObject.Code(self.db)
|
|
|
}
|
|
|
-
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+func (self *StateDB) GetCodeSize(addr common.Address) int {
|
|
|
+ stateObject := self.GetStateObject(addr)
|
|
|
+ if stateObject != nil {
|
|
|
+ return stateObject.CodeSize(self.db)
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+}
|
|
|
+
|
|
|
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
|
|
stateObject := self.GetStateObject(a)
|
|
|
if stateObject != nil {
|
|
|
- return stateObject.GetState(b)
|
|
|
+ return stateObject.GetState(self.db, b)
|
|
|
}
|
|
|
-
|
|
|
return common.Hash{}
|
|
|
}
|
|
|
|
|
|
@@ -214,8 +230,7 @@ func (self *StateDB) Delete(addr common.Address) bool {
|
|
|
stateObject := self.GetStateObject(addr)
|
|
|
if stateObject != nil {
|
|
|
stateObject.MarkForDeletion()
|
|
|
- stateObject.balance = new(big.Int)
|
|
|
-
|
|
|
+ stateObject.data.Balance = new(big.Int)
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
@@ -242,35 +257,47 @@ func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
|
|
|
|
|
addr := stateObject.Address()
|
|
|
self.trie.Delete(addr[:])
|
|
|
- //delete(self.stateObjects, addr.Str())
|
|
|
}
|
|
|
|
|
|
-// Retrieve a state object given my the address. Nil if not found
|
|
|
+// Retrieve a state object given my the address. Returns nil if not found.
|
|
|
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
|
|
- stateObject = self.stateObjects[addr.Str()]
|
|
|
- if stateObject != nil {
|
|
|
- if stateObject.deleted {
|
|
|
- stateObject = nil
|
|
|
+ // Prefer 'live' objects.
|
|
|
+ if obj := self.stateObjects[addr]; obj != nil {
|
|
|
+ if obj.deleted {
|
|
|
+ return nil
|
|
|
}
|
|
|
+ return obj
|
|
|
+ }
|
|
|
|
|
|
- return stateObject
|
|
|
+ // Use cached account data from the canon state if possible.
|
|
|
+ if data, ok := self.all[addr]; ok {
|
|
|
+ obj := NewObject(addr, data, self.MarkStateObjectDirty)
|
|
|
+ self.SetStateObject(obj)
|
|
|
+ return obj
|
|
|
}
|
|
|
|
|
|
- data := self.trie.Get(addr[:])
|
|
|
- if len(data) == 0 {
|
|
|
+ // Load the object from the database.
|
|
|
+ enc := self.trie.Get(addr[:])
|
|
|
+ if len(enc) == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
- stateObject, err := DecodeObject(addr, self.db, data)
|
|
|
- if err != nil {
|
|
|
+ var data Account
|
|
|
+ if err := rlp.DecodeBytes(enc, &data); err != nil {
|
|
|
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
|
|
return nil
|
|
|
}
|
|
|
- self.SetStateObject(stateObject)
|
|
|
- return stateObject
|
|
|
+ // Update the all cache. Content in DB always corresponds
|
|
|
+ // to the current head state so this is ok to do here.
|
|
|
+ // The object we just loaded has no storage trie and code yet.
|
|
|
+ self.all[addr] = data
|
|
|
+ // Insert into the live set.
|
|
|
+ obj := NewObject(addr, data, self.MarkStateObjectDirty)
|
|
|
+ self.SetStateObject(obj)
|
|
|
+ return obj
|
|
|
}
|
|
|
|
|
|
func (self *StateDB) SetStateObject(object *StateObject) {
|
|
|
- self.stateObjects[object.Address().Str()] = object
|
|
|
+ self.stateObjects[object.Address()] = object
|
|
|
}
|
|
|
|
|
|
// Retrieve a state object or create a new state object if nil
|
|
|
@@ -288,15 +315,19 @@ func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
|
|
if glog.V(logger.Core) {
|
|
|
glog.Infof("(+) %x\n", addr)
|
|
|
}
|
|
|
+ obj := NewObject(addr, Account{}, self.MarkStateObjectDirty)
|
|
|
+ obj.SetNonce(StartingNonce) // sets the object to dirty
|
|
|
+ self.stateObjects[addr] = obj
|
|
|
+ return obj
|
|
|
+}
|
|
|
|
|
|
- stateObject := NewStateObject(addr, self.db)
|
|
|
- stateObject.SetNonce(StartingNonce)
|
|
|
- self.stateObjects[addr.Str()] = stateObject
|
|
|
-
|
|
|
- return stateObject
|
|
|
+// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
|
|
|
+// state object cache iteration to find a handful of modified ones.
|
|
|
+func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
|
|
|
+ self.stateObjectsDirty[addr] = struct{}{}
|
|
|
}
|
|
|
|
|
|
-// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
|
|
|
+// Creates creates a new state object and takes ownership.
|
|
|
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
|
|
// Get previous (if any)
|
|
|
so := self.GetStateObject(addr)
|
|
|
@@ -305,7 +336,7 @@ func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
|
|
|
|
|
// If it existed set the balance to the new account
|
|
|
if so != nil {
|
|
|
- newSo.balance = so.balance
|
|
|
+ newSo.data.Balance = so.data.Balance
|
|
|
}
|
|
|
|
|
|
return newSo
|
|
|
@@ -320,29 +351,34 @@ func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
|
|
|
//
|
|
|
|
|
|
func (self *StateDB) Copy() *StateDB {
|
|
|
- // ignore error - we assume state-to-be-copied always exists
|
|
|
- state, _ := New(common.Hash{}, self.db)
|
|
|
- state.trie = self.trie
|
|
|
- for k, stateObject := range self.stateObjects {
|
|
|
- if stateObject.dirty {
|
|
|
- state.stateObjects[k] = stateObject.Copy()
|
|
|
- }
|
|
|
+ // Copy all the basic fields, initialize the memory ones
|
|
|
+ state := &StateDB{
|
|
|
+ db: self.db,
|
|
|
+ trie: self.trie,
|
|
|
+ all: self.all,
|
|
|
+ stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
|
|
|
+ stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
|
|
|
+ refund: new(big.Int).Set(self.refund),
|
|
|
+ logs: make(map[common.Hash]vm.Logs, len(self.logs)),
|
|
|
+ logSize: self.logSize,
|
|
|
+ }
|
|
|
+ // Copy the dirty states and logs
|
|
|
+ for addr, _ := range self.stateObjectsDirty {
|
|
|
+ state.stateObjects[addr] = self.stateObjects[addr].Copy(self.db, state.MarkStateObjectDirty)
|
|
|
+ state.stateObjectsDirty[addr] = struct{}{}
|
|
|
}
|
|
|
-
|
|
|
- state.refund.Set(self.refund)
|
|
|
-
|
|
|
for hash, logs := range self.logs {
|
|
|
state.logs[hash] = make(vm.Logs, len(logs))
|
|
|
copy(state.logs[hash], logs)
|
|
|
}
|
|
|
- state.logSize = self.logSize
|
|
|
-
|
|
|
return state
|
|
|
}
|
|
|
|
|
|
func (self *StateDB) Set(state *StateDB) {
|
|
|
self.trie = state.trie
|
|
|
self.stateObjects = state.stateObjects
|
|
|
+ self.stateObjectsDirty = state.stateObjectsDirty
|
|
|
+ self.all = state.all
|
|
|
|
|
|
self.refund = state.refund
|
|
|
self.logs = state.logs
|
|
|
@@ -358,14 +394,13 @@ func (self *StateDB) GetRefund() *big.Int {
|
|
|
// goes into transaction receipts.
|
|
|
func (s *StateDB) IntermediateRoot() common.Hash {
|
|
|
s.refund = new(big.Int)
|
|
|
- for _, stateObject := range s.stateObjects {
|
|
|
- if stateObject.dirty {
|
|
|
- if stateObject.remove {
|
|
|
- s.DeleteStateObject(stateObject)
|
|
|
- } else {
|
|
|
- stateObject.Update()
|
|
|
- s.UpdateStateObject(stateObject)
|
|
|
- }
|
|
|
+ for addr, _ := range s.stateObjectsDirty {
|
|
|
+ stateObject := s.stateObjects[addr]
|
|
|
+ if stateObject.remove {
|
|
|
+ s.DeleteStateObject(stateObject)
|
|
|
+ } else {
|
|
|
+ stateObject.UpdateRoot(s.db)
|
|
|
+ s.UpdateStateObject(stateObject)
|
|
|
}
|
|
|
}
|
|
|
return s.trie.Hash()
|
|
|
@@ -380,15 +415,15 @@ func (s *StateDB) DeleteSuicides() {
|
|
|
// Reset refund so that any used-gas calculations can use
|
|
|
// this method.
|
|
|
s.refund = new(big.Int)
|
|
|
- for _, stateObject := range s.stateObjects {
|
|
|
- if stateObject.dirty {
|
|
|
- // If the object has been removed by a suicide
|
|
|
- // flag the object as deleted.
|
|
|
- if stateObject.remove {
|
|
|
- stateObject.deleted = true
|
|
|
- }
|
|
|
- stateObject.dirty = false
|
|
|
+ for addr, _ := range s.stateObjectsDirty {
|
|
|
+ stateObject := s.stateObjects[addr]
|
|
|
+
|
|
|
+ // If the object has been removed by a suicide
|
|
|
+ // flag the object as deleted.
|
|
|
+ if stateObject.remove {
|
|
|
+ stateObject.deleted = true
|
|
|
}
|
|
|
+ delete(s.stateObjectsDirty, addr)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -407,46 +442,44 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
|
|
return root, batch
|
|
|
}
|
|
|
|
|
|
-func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
|
|
|
+func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) {
|
|
|
s.refund = new(big.Int)
|
|
|
+ defer func() {
|
|
|
+ if err != nil {
|
|
|
+ // Committing failed, any updates to the canon state are invalid.
|
|
|
+ s.all = make(map[common.Address]Account)
|
|
|
+ }
|
|
|
+ }()
|
|
|
|
|
|
- for _, stateObject := range s.stateObjects {
|
|
|
+ // Commit objects to the trie.
|
|
|
+ for addr, stateObject := range s.stateObjects {
|
|
|
if stateObject.remove {
|
|
|
// If the object has been removed, don't bother syncing it
|
|
|
// and just mark it for deletion in the trie.
|
|
|
s.DeleteStateObject(stateObject)
|
|
|
- } else {
|
|
|
+ delete(s.all, addr)
|
|
|
+ } else if _, ok := s.stateObjectsDirty[addr]; ok {
|
|
|
// Write any contract code associated with the state object
|
|
|
- if len(stateObject.code) > 0 {
|
|
|
- if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
|
|
|
+ if stateObject.code != nil && stateObject.dirtyCode {
|
|
|
+ if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
|
|
|
return common.Hash{}, err
|
|
|
}
|
|
|
+ stateObject.dirtyCode = false
|
|
|
}
|
|
|
- // Write any storage changes in the state object to its trie.
|
|
|
- stateObject.Update()
|
|
|
-
|
|
|
- // Commit the trie of the object to the batch.
|
|
|
- // This updates the trie root internally, so
|
|
|
- // getting the root hash of the storage trie
|
|
|
- // through UpdateStateObject is fast.
|
|
|
- if _, err := stateObject.trie.CommitTo(db); err != nil {
|
|
|
+ // Write any storage changes in the state object to its storage trie.
|
|
|
+ if err := stateObject.CommitTrie(s.db, dbw); err != nil {
|
|
|
return common.Hash{}, err
|
|
|
}
|
|
|
- // Update the object in the account trie.
|
|
|
+ // Update the object in the main account trie.
|
|
|
s.UpdateStateObject(stateObject)
|
|
|
+ s.all[addr] = stateObject.data
|
|
|
}
|
|
|
- stateObject.dirty = false
|
|
|
+ delete(s.stateObjectsDirty, addr)
|
|
|
}
|
|
|
- return s.trie.CommitTo(db)
|
|
|
+ // Write trie changes.
|
|
|
+ return s.trie.CommitTo(dbw)
|
|
|
}
|
|
|
|
|
|
func (self *StateDB) Refunds() *big.Int {
|
|
|
return self.refund
|
|
|
}
|
|
|
-
|
|
|
-// Debug stuff
|
|
|
-func (self *StateDB) CreateOutputForDiff() {
|
|
|
- for _, stateObject := range self.stateObjects {
|
|
|
- stateObject.CreateOutputForDiff()
|
|
|
- }
|
|
|
-}
|