github.com/ethereum-optimism/optimism/l2geth@v0.0.0-20230612200230-50b04ade19e3/core/state/journal.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package state 18 19 import ( 20 "math/big" 21 22 "github.com/ethereum-optimism/optimism/l2geth/common" 23 ) 24 25 // journalEntry is a modification entry in the state change journal that can be 26 // reverted on demand. 27 type journalEntry interface { 28 // revert undoes the changes introduced by this journal entry. 29 revert(*StateDB) 30 31 // dirtied returns the Ethereum address modified by this journal entry. 32 dirtied() *common.Address 33 } 34 35 // journal contains the list of state modifications applied since the last state 36 // commit. These are tracked to be able to be reverted in case of an execution 37 // exception or revertal request. 38 type journal struct { 39 entries []journalEntry // Current changes tracked by the journal 40 dirties map[common.Address]int // Dirty accounts and the number of changes 41 } 42 43 // newJournal create a new initialized journal. 44 func newJournal() *journal { 45 return &journal{ 46 dirties: make(map[common.Address]int), 47 } 48 } 49 50 // append inserts a new modification entry to the end of the change journal. 51 func (j *journal) append(entry journalEntry) { 52 j.entries = append(j.entries, entry) 53 if addr := entry.dirtied(); addr != nil { 54 j.dirties[*addr]++ 55 } 56 } 57 58 // revert undoes a batch of journalled modifications along with any reverted 59 // dirty handling too. 60 func (j *journal) revert(statedb *StateDB, snapshot int) { 61 for i := len(j.entries) - 1; i >= snapshot; i-- { 62 // Undo the changes made by the operation 63 j.entries[i].revert(statedb) 64 65 // Drop any dirty tracking induced by the change 66 if addr := j.entries[i].dirtied(); addr != nil { 67 if j.dirties[*addr]--; j.dirties[*addr] == 0 { 68 delete(j.dirties, *addr) 69 } 70 } 71 } 72 j.entries = j.entries[:snapshot] 73 } 74 75 // dirty explicitly sets an address to dirty, even if the change entries would 76 // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD 77 // precompile consensus exception. 78 func (j *journal) dirty(addr common.Address) { 79 j.dirties[addr]++ 80 } 81 82 // length returns the current number of entries in the journal. 83 func (j *journal) length() int { 84 return len(j.entries) 85 } 86 87 type ( 88 // Changes to the account trie. 89 createObjectChange struct { 90 account *common.Address 91 } 92 resetObjectChange struct { 93 prev *stateObject 94 } 95 suicideChange struct { 96 account *common.Address 97 prev bool // whether account had already suicided 98 prevbalance *big.Int 99 } 100 101 // Changes to individual accounts. 102 balanceChange struct { 103 account *common.Address 104 prev *big.Int 105 } 106 nonceChange struct { 107 account *common.Address 108 prev uint64 109 } 110 storageChange struct { 111 account *common.Address 112 key, prevalue common.Hash 113 } 114 codeChange struct { 115 account *common.Address 116 prevcode, prevhash []byte 117 } 118 119 // Changes to other state values. 120 refundChange struct { 121 prev uint64 122 } 123 addLogChange struct { 124 txhash common.Hash 125 } 126 addPreimageChange struct { 127 hash common.Hash 128 } 129 touchChange struct { 130 account *common.Address 131 } 132 133 // Changes to the access list 134 accessListAddAccountChange struct { 135 address *common.Address 136 } 137 accessListAddSlotChange struct { 138 address *common.Address 139 slot *common.Hash 140 } 141 ) 142 143 func (ch createObjectChange) revert(s *StateDB) { 144 delete(s.stateObjects, *ch.account) 145 delete(s.stateObjectsDirty, *ch.account) 146 } 147 148 func (ch createObjectChange) dirtied() *common.Address { 149 return ch.account 150 } 151 152 func (ch resetObjectChange) revert(s *StateDB) { 153 s.setStateObject(ch.prev) 154 } 155 156 func (ch resetObjectChange) dirtied() *common.Address { 157 return nil 158 } 159 160 func (ch suicideChange) revert(s *StateDB) { 161 obj := s.getStateObject(*ch.account) 162 if obj != nil { 163 obj.suicided = ch.prev 164 obj.setBalance(ch.prevbalance) 165 } 166 } 167 168 func (ch suicideChange) dirtied() *common.Address { 169 return ch.account 170 } 171 172 var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") 173 174 func (ch touchChange) revert(s *StateDB) { 175 } 176 177 func (ch touchChange) dirtied() *common.Address { 178 return ch.account 179 } 180 181 func (ch balanceChange) revert(s *StateDB) { 182 s.getStateObject(*ch.account).setBalance(ch.prev) 183 } 184 185 func (ch balanceChange) dirtied() *common.Address { 186 return ch.account 187 } 188 189 func (ch nonceChange) revert(s *StateDB) { 190 s.getStateObject(*ch.account).setNonce(ch.prev) 191 } 192 193 func (ch nonceChange) dirtied() *common.Address { 194 return ch.account 195 } 196 197 func (ch codeChange) revert(s *StateDB) { 198 s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode) 199 } 200 201 func (ch codeChange) dirtied() *common.Address { 202 return ch.account 203 } 204 205 func (ch storageChange) revert(s *StateDB) { 206 s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) 207 } 208 209 func (ch storageChange) dirtied() *common.Address { 210 return ch.account 211 } 212 213 func (ch refundChange) revert(s *StateDB) { 214 s.refund = ch.prev 215 } 216 217 func (ch refundChange) dirtied() *common.Address { 218 return nil 219 } 220 221 func (ch addLogChange) revert(s *StateDB) { 222 logs := s.logs[ch.txhash] 223 if len(logs) == 1 { 224 delete(s.logs, ch.txhash) 225 } else { 226 s.logs[ch.txhash] = logs[:len(logs)-1] 227 } 228 s.logSize-- 229 } 230 231 func (ch addLogChange) dirtied() *common.Address { 232 return nil 233 } 234 235 func (ch addPreimageChange) revert(s *StateDB) { 236 delete(s.preimages, ch.hash) 237 } 238 239 func (ch addPreimageChange) dirtied() *common.Address { 240 return nil 241 } 242 243 func (ch accessListAddAccountChange) revert(s *StateDB) { 244 /* 245 One important invariant here, is that whenever a (addr, slot) is added, if the 246 addr is not already present, the add causes two journal entries: 247 - one for the address, 248 - one for the (address,slot) 249 Therefore, when unrolling the change, we can always blindly delete the 250 (addr) at this point, since no storage adds can remain when come upon 251 a single (addr) change. 252 */ 253 s.accessList.DeleteAddress(*ch.address) 254 } 255 256 func (ch accessListAddAccountChange) dirtied() *common.Address { 257 return nil 258 } 259 260 func (ch accessListAddSlotChange) revert(s *StateDB) { 261 s.accessList.DeleteSlot(*ch.address, *ch.slot) 262 } 263 264 func (ch accessListAddSlotChange) dirtied() *common.Address { 265 return nil 266 }