aboutsummaryrefslogtreecommitdiffstats
path: root/core/state/journal.go
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2018-03-27 20:13:30 +0800
committerPéter Szilágyi <peterke@gmail.com>2018-03-28 14:32:02 +0800
commitd985b9052ae08f2538e6caa7e6b5ef351a00bc3e (patch)
treec1c638b59b210c666d2c42dcf9a8edc341e493e9 /core/state/journal.go
parent958ed4f3d977b08465915e475e11aaab3d2dc574 (diff)
downloadgo-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar.gz
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar.bz2
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar.lz
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar.xz
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.tar.zst
go-tangerine-d985b9052ae08f2538e6caa7e6b5ef351a00bc3e.zip
core/state: avoid linear overhead on journal dirty listing
Diffstat (limited to 'core/state/journal.go')
-rw-r--r--core/state/journal.go113
1 files changed, 73 insertions, 40 deletions
diff --git a/core/state/journal.go b/core/state/journal.go
index b00a46224..a03ca57db 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -22,41 +22,66 @@ import (
"github.com/ethereum/go-ethereum/common"
)
+// journalEntry is a modification entry in the state change journal that can be
+// reverted on demand.
type journalEntry interface {
- undo(*StateDB)
- getAccount() *common.Address
+ // 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
- dirtyOverrides []common.Address
+ 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]++
+ }
}
-func (j *journal) flatten() map[common.Address]struct{} {
+// 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)
- dirtyObjects := make(map[common.Address]struct{})
- for _, journalEntry := range j.entries {
- if addr := journalEntry.getAccount(); addr != nil {
- dirtyObjects[*addr] = struct{}{}
+ // 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)
+ }
}
}
- for _, addr := range j.dirtyOverrides {
- dirtyObjects[addr] = struct{}{}
- }
- return dirtyObjects
+ j.entries = j.entries[:snapshot]
}
-// Length returns the number of journal entries in the journal
-func (j *journal) Length() int {
- return len(j.entries)
+// 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]++
}
-func (j *journal) dirtyOverride(address common.Address) {
- j.dirtyOverrides = append(j.dirtyOverrides, address)
+// length returns the current number of entries in the journal.
+func (j *journal) length() int {
+ return len(j.entries)
}
type (
@@ -108,78 +133,85 @@ type (
}
)
-func (ch createObjectChange) undo(s *StateDB) {
+func (ch createObjectChange) revert(s *StateDB) {
delete(s.stateObjects, *ch.account)
delete(s.stateObjectsDirty, *ch.account)
}
-func (ch createObjectChange) getAccount() *common.Address {
+func (ch createObjectChange) dirtied() *common.Address {
return ch.account
}
-func (ch resetObjectChange) undo(s *StateDB) {
+func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev)
}
-func (ch resetObjectChange) getAccount() *common.Address {
+func (ch resetObjectChange) dirtied() *common.Address {
return nil
}
-func (ch suicideChange) undo(s *StateDB) {
+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) getAccount() *common.Address {
+
+func (ch suicideChange) dirtied() *common.Address {
return ch.account
}
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
-func (ch touchChange) undo(s *StateDB) {
+func (ch touchChange) revert(s *StateDB) {
}
-func (ch touchChange) getAccount() *common.Address {
+
+func (ch touchChange) dirtied() *common.Address {
return ch.account
}
-func (ch balanceChange) undo(s *StateDB) {
+func (ch balanceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
-func (ch balanceChange) getAccount() *common.Address {
+
+func (ch balanceChange) dirtied() *common.Address {
return ch.account
}
-func (ch nonceChange) undo(s *StateDB) {
+func (ch nonceChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}
-func (ch nonceChange) getAccount() *common.Address {
+func (ch nonceChange) dirtied() *common.Address {
return ch.account
}
-func (ch codeChange) undo(s *StateDB) {
+
+func (ch codeChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
-func (ch codeChange) getAccount() *common.Address {
+
+func (ch codeChange) dirtied() *common.Address {
return ch.account
}
-func (ch storageChange) undo(s *StateDB) {
+func (ch storageChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
-func (ch storageChange) getAccount() *common.Address {
+
+func (ch storageChange) dirtied() *common.Address {
return ch.account
}
-func (ch refundChange) undo(s *StateDB) {
+func (ch refundChange) revert(s *StateDB) {
s.refund = ch.prev
}
-func (ch refundChange) getAccount() *common.Address {
+
+func (ch refundChange) dirtied() *common.Address {
return nil
}
-func (ch addLogChange) undo(s *StateDB) {
+func (ch addLogChange) revert(s *StateDB) {
logs := s.logs[ch.txhash]
if len(logs) == 1 {
delete(s.logs, ch.txhash)
@@ -188,14 +220,15 @@ func (ch addLogChange) undo(s *StateDB) {
}
s.logSize--
}
-func (ch addLogChange) getAccount() *common.Address {
+
+func (ch addLogChange) dirtied() *common.Address {
return nil
}
-func (ch addPreimageChange) undo(s *StateDB) {
+func (ch addPreimageChange) revert(s *StateDB) {
delete(s.preimages, ch.hash)
}
-func (ch addPreimageChange) getAccount() *common.Address {
+func (ch addPreimageChange) dirtied() *common.Address {
return nil
}