diff options
author | Péter Szilágyi <peterke@gmail.com> | 2018-03-27 20:13:30 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2018-03-28 14:32:02 +0800 |
commit | d985b9052ae08f2538e6caa7e6b5ef351a00bc3e (patch) | |
tree | c1c638b59b210c666d2c42dcf9a8edc341e493e9 /core/state/journal.go | |
parent | 958ed4f3d977b08465915e475e11aaab3d2dc574 (diff) | |
download | go-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.go | 113 |
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 } |