Skip to content

Commit

Permalink
core/state: rework dirty handling to avoid quadratic overhead
Browse files Browse the repository at this point in the history
  • Loading branch information
holiman authored and karalabe committed Mar 28, 2018
1 parent 1a8894b commit 958ed4f
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 79 deletions.
2 changes: 1 addition & 1 deletion core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (self *StateDB) RawDump() Dump {
panic(err)
}

obj := newObject(nil, common.BytesToAddress(addr), data, nil)
obj := newObject(nil, common.BytesToAddress(addr), data)
account := DumpAccount{
Balance: data.Balance.String(),
Nonce: data.Nonce,
Expand Down
75 changes: 68 additions & 7 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,40 @@ import (

type journalEntry interface {
undo(*StateDB)
getAccount() *common.Address
}

type journal []journalEntry
type journal struct {
entries []journalEntry
dirtyOverrides []common.Address
}

func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry)
}

func (j *journal) flatten() map[common.Address]struct{} {

dirtyObjects := make(map[common.Address]struct{})
for _, journalEntry := range j.entries {
if addr := journalEntry.getAccount(); addr != nil {
dirtyObjects[*addr] = struct{}{}
}
}
for _, addr := range j.dirtyOverrides {
dirtyObjects[addr] = struct{}{}
}
return dirtyObjects
}

// Length returns the number of journal entries in the journal
func (j *journal) Length() int {
return len(j.entries)
}

func (j *journal) dirtyOverride(address common.Address) {
j.dirtyOverrides = append(j.dirtyOverrides, address)
}

type (
// Changes to the account trie.
Expand Down Expand Up @@ -82,48 +113,71 @@ func (ch createObjectChange) undo(s *StateDB) {
delete(s.stateObjectsDirty, *ch.account)
}

func (ch createObjectChange) getAccount() *common.Address {
return ch.account
}

func (ch resetObjectChange) undo(s *StateDB) {
s.setStateObject(ch.prev)
}

func (ch resetObjectChange) getAccount() *common.Address {
return nil
}

func (ch suicideChange) undo(s *StateDB) {
obj := s.getStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
obj.setBalance(ch.prevbalance)
}
}
func (ch suicideChange) getAccount() *common.Address {
return ch.account
}

var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")

func (ch touchChange) undo(s *StateDB) {
if !ch.prev && *ch.account != ripemd {
s.getStateObject(*ch.account).touched = ch.prev
if !ch.prevDirty {
delete(s.stateObjectsDirty, *ch.account)
}
}
}
func (ch touchChange) getAccount() *common.Address {
return ch.account
}

func (ch balanceChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch balanceChange) getAccount() *common.Address {
return ch.account
}

func (ch nonceChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}

func (ch nonceChange) getAccount() *common.Address {
return ch.account
}
func (ch codeChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch codeChange) getAccount() *common.Address {
return ch.account
}

func (ch storageChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch storageChange) getAccount() *common.Address {
return ch.account
}

func (ch refundChange) undo(s *StateDB) {
s.refund = ch.prev
}
func (ch refundChange) getAccount() *common.Address {
return nil
}

func (ch addLogChange) undo(s *StateDB) {
logs := s.logs[ch.txhash]
Expand All @@ -134,7 +188,14 @@ func (ch addLogChange) undo(s *StateDB) {
}
s.logSize--
}
func (ch addLogChange) getAccount() *common.Address {
return nil
}

func (ch addPreimageChange) undo(s *StateDB) {
delete(s.preimages, ch.hash)
}

func (ch addPreimageChange) getAccount() *common.Address {
return nil
}
51 changes: 13 additions & 38 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ type stateObject struct {
// during the "update" phase of the state transition.
dirtyCode bool // true if the code was updated
suicided bool
touched bool
deleted bool
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
}

// empty returns whether the account is considered empty.
Expand All @@ -105,7 +103,7 @@ type Account struct {
}

// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject {
func newObject(db *StateDB, address common.Address, data Account) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
Expand All @@ -119,7 +117,6 @@ func newObject(db *StateDB, address common.Address, data Account, onDirty func(a
data: data,
cachedStorage: make(Storage),
dirtyStorage: make(Storage),
onDirty: onDirty,
}
}

Expand All @@ -137,23 +134,17 @@ func (self *stateObject) setError(err error) {

func (self *stateObject) markSuicided() {
self.suicided = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}

func (c *stateObject) touch() {
c.db.journal = append(c.db.journal, touchChange{
account: &c.address,
prev: c.touched,
prevDirty: c.onDirty == nil,
c.db.journal.append(touchChange{
account: &c.address,
})
if c.onDirty != nil {
c.onDirty(c.Address())
c.onDirty = nil
if c.address == ripemd {
//Explicitly put it in the dirty-cache, which is otherwise
// generated from flattened journals
c.db.journal.dirtyOverride(c.address)
}
c.touched = true
}

func (c *stateObject) getTrie(db Database) Trie {
Expand Down Expand Up @@ -195,7 +186,7 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {

// SetState updates a value in account storage.
func (self *stateObject) SetState(db Database, key, value common.Hash) {
self.db.journal = append(self.db.journal, storageChange{
self.db.journal.append(storageChange{
account: &self.address,
key: key,
prevalue: self.GetState(db, key),
Expand All @@ -207,10 +198,6 @@ func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value

if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}

// updateTrie writes cached storage modifications into the object's storage trie.
Expand Down Expand Up @@ -274,7 +261,7 @@ func (c *stateObject) SubBalance(amount *big.Int) {
}

func (self *stateObject) SetBalance(amount *big.Int) {
self.db.journal = append(self.db.journal, balanceChange{
self.db.journal.append(balanceChange{
account: &self.address,
prev: new(big.Int).Set(self.data.Balance),
})
Expand All @@ -283,17 +270,13 @@ func (self *stateObject) SetBalance(amount *big.Int) {

func (self *stateObject) setBalance(amount *big.Int) {
self.data.Balance = amount
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}

// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *stateObject) ReturnGas(gas *big.Int) {}

func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
func (self *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject := newObject(db, self.address, self.data)
if self.trie != nil {
stateObject.trie = db.db.CopyTrie(self.trie)
}
Expand Down Expand Up @@ -333,7 +316,7 @@ func (self *stateObject) Code(db Database) []byte {

func (self *stateObject) SetCode(codeHash common.Hash, code []byte) {
prevcode := self.Code(self.db.db)
self.db.journal = append(self.db.journal, codeChange{
self.db.journal.append(codeChange{
account: &self.address,
prevhash: self.CodeHash(),
prevcode: prevcode,
Expand All @@ -345,14 +328,10 @@ func (self *stateObject) setCode(codeHash common.Hash, code []byte) {
self.code = code
self.data.CodeHash = codeHash[:]
self.dirtyCode = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}

func (self *stateObject) SetNonce(nonce uint64) {
self.db.journal = append(self.db.journal, nonceChange{
self.db.journal.append(nonceChange{
account: &self.address,
prev: self.data.Nonce,
})
Expand All @@ -361,10 +340,6 @@ func (self *stateObject) SetNonce(nonce uint64) {

func (self *stateObject) setNonce(nonce uint64) {
self.data.Nonce = nonce
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}

func (self *stateObject) CodeHash() []byte {
Expand Down
Loading

0 comments on commit 958ed4f

Please sign in to comment.