github.com/ethereum/go-ethereum@v1.16.1/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 "fmt" 21 "maps" 22 "slices" 23 "sort" 24 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/crypto" 27 "github.com/holiman/uint256" 28 ) 29 30 type revision struct { 31 id int 32 journalIndex int 33 } 34 35 // journalEntry is a modification entry in the state change journal that can be 36 // reverted on demand. 37 type journalEntry interface { 38 // revert undoes the changes introduced by this journal entry. 39 revert(*StateDB) 40 41 // dirtied returns the Ethereum address modified by this journal entry. 42 dirtied() *common.Address 43 44 // copy returns a deep-copied journal entry. 45 copy() journalEntry 46 } 47 48 // journal contains the list of state modifications applied since the last state 49 // commit. These are tracked to be able to be reverted in the case of an execution 50 // exception or request for reversal. 51 type journal struct { 52 entries []journalEntry // Current changes tracked by the journal 53 dirties map[common.Address]int // Dirty accounts and the number of changes 54 55 validRevisions []revision 56 nextRevisionId int 57 } 58 59 // newJournal creates a new initialized journal. 60 func newJournal() *journal { 61 return &journal{ 62 dirties: make(map[common.Address]int), 63 } 64 } 65 66 // reset clears the journal, after this operation the journal can be used anew. 67 // It is semantically similar to calling 'newJournal', but the underlying slices 68 // can be reused. 69 func (j *journal) reset() { 70 j.entries = j.entries[:0] 71 j.validRevisions = j.validRevisions[:0] 72 clear(j.dirties) 73 j.nextRevisionId = 0 74 } 75 76 // snapshot returns an identifier for the current revision of the state. 77 func (j *journal) snapshot() int { 78 id := j.nextRevisionId 79 j.nextRevisionId++ 80 j.validRevisions = append(j.validRevisions, revision{id, j.length()}) 81 return id 82 } 83 84 // revertToSnapshot reverts all state changes made since the given revision. 85 func (j *journal) revertToSnapshot(revid int, s *StateDB) { 86 // Find the snapshot in the stack of valid snapshots. 87 idx := sort.Search(len(j.validRevisions), func(i int) bool { 88 return j.validRevisions[i].id >= revid 89 }) 90 if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid { 91 panic(fmt.Errorf("revision id %v cannot be reverted", revid)) 92 } 93 snapshot := j.validRevisions[idx].journalIndex 94 95 // Replay the journal to undo changes and remove invalidated snapshots 96 j.revert(s, snapshot) 97 j.validRevisions = j.validRevisions[:idx] 98 } 99 100 // append inserts a new modification entry to the end of the change journal. 101 func (j *journal) append(entry journalEntry) { 102 j.entries = append(j.entries, entry) 103 if addr := entry.dirtied(); addr != nil { 104 j.dirties[*addr]++ 105 } 106 } 107 108 // revert undoes a batch of journalled modifications along with any reverted 109 // dirty handling too. 110 func (j *journal) revert(statedb *StateDB, snapshot int) { 111 for i := len(j.entries) - 1; i >= snapshot; i-- { 112 // Undo the changes made by the operation 113 j.entries[i].revert(statedb) 114 115 // Drop any dirty tracking induced by the change 116 if addr := j.entries[i].dirtied(); addr != nil { 117 if j.dirties[*addr]--; j.dirties[*addr] == 0 { 118 delete(j.dirties, *addr) 119 } 120 } 121 } 122 j.entries = j.entries[:snapshot] 123 } 124 125 // dirty explicitly sets an address to dirty, even if the change entries would 126 // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD 127 // precompile consensus exception. 128 func (j *journal) dirty(addr common.Address) { 129 j.dirties[addr]++ 130 } 131 132 // length returns the current number of entries in the journal. 133 func (j *journal) length() int { 134 return len(j.entries) 135 } 136 137 // copy returns a deep-copied journal. 138 func (j *journal) copy() *journal { 139 entries := make([]journalEntry, 0, j.length()) 140 for i := 0; i < j.length(); i++ { 141 entries = append(entries, j.entries[i].copy()) 142 } 143 return &journal{ 144 entries: entries, 145 dirties: maps.Clone(j.dirties), 146 validRevisions: slices.Clone(j.validRevisions), 147 nextRevisionId: j.nextRevisionId, 148 } 149 } 150 151 func (j *journal) logChange(txHash common.Hash) { 152 j.append(addLogChange{txhash: txHash}) 153 } 154 155 func (j *journal) createObject(addr common.Address) { 156 j.append(createObjectChange{account: addr}) 157 } 158 159 func (j *journal) createContract(addr common.Address) { 160 j.append(createContractChange{account: addr}) 161 } 162 163 func (j *journal) destruct(addr common.Address) { 164 j.append(selfDestructChange{account: addr}) 165 } 166 167 func (j *journal) storageChange(addr common.Address, key, prev, origin common.Hash) { 168 j.append(storageChange{ 169 account: addr, 170 key: key, 171 prevvalue: prev, 172 origvalue: origin, 173 }) 174 } 175 176 func (j *journal) transientStateChange(addr common.Address, key, prev common.Hash) { 177 j.append(transientStorageChange{ 178 account: addr, 179 key: key, 180 prevalue: prev, 181 }) 182 } 183 184 func (j *journal) refundChange(previous uint64) { 185 j.append(refundChange{prev: previous}) 186 } 187 188 func (j *journal) balanceChange(addr common.Address, previous *uint256.Int) { 189 j.append(balanceChange{ 190 account: addr, 191 prev: previous.Clone(), 192 }) 193 } 194 195 func (j *journal) setCode(address common.Address, prevCode []byte) { 196 j.append(codeChange{ 197 account: address, 198 prevCode: prevCode, 199 }) 200 } 201 202 func (j *journal) nonceChange(address common.Address, prev uint64) { 203 j.append(nonceChange{ 204 account: address, 205 prev: prev, 206 }) 207 } 208 209 func (j *journal) touchChange(address common.Address) { 210 j.append(touchChange{ 211 account: address, 212 }) 213 if address == ripemd { 214 // Explicitly put it in the dirty-cache, which is otherwise generated from 215 // flattened journals. 216 j.dirty(address) 217 } 218 } 219 220 func (j *journal) accessListAddAccount(addr common.Address) { 221 j.append(accessListAddAccountChange{addr}) 222 } 223 224 func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) { 225 j.append(accessListAddSlotChange{ 226 address: addr, 227 slot: slot, 228 }) 229 } 230 231 type ( 232 // Changes to the account trie. 233 createObjectChange struct { 234 account common.Address 235 } 236 // createContractChange represents an account becoming a contract-account. 237 // This event happens prior to executing initcode. The journal-event simply 238 // manages the created-flag, in order to allow same-tx destruction. 239 createContractChange struct { 240 account common.Address 241 } 242 selfDestructChange struct { 243 account common.Address 244 } 245 246 // Changes to individual accounts. 247 balanceChange struct { 248 account common.Address 249 prev *uint256.Int 250 } 251 nonceChange struct { 252 account common.Address 253 prev uint64 254 } 255 storageChange struct { 256 account common.Address 257 key common.Hash 258 prevvalue common.Hash 259 origvalue common.Hash 260 } 261 codeChange struct { 262 account common.Address 263 prevCode []byte 264 } 265 266 // Changes to other state values. 267 refundChange struct { 268 prev uint64 269 } 270 addLogChange struct { 271 txhash common.Hash 272 } 273 touchChange struct { 274 account common.Address 275 } 276 277 // Changes to the access list 278 accessListAddAccountChange struct { 279 address common.Address 280 } 281 accessListAddSlotChange struct { 282 address common.Address 283 slot common.Hash 284 } 285 286 // Changes to transient storage 287 transientStorageChange struct { 288 account common.Address 289 key, prevalue common.Hash 290 } 291 ) 292 293 func (ch createObjectChange) revert(s *StateDB) { 294 delete(s.stateObjects, ch.account) 295 } 296 297 func (ch createObjectChange) dirtied() *common.Address { 298 return &ch.account 299 } 300 301 func (ch createObjectChange) copy() journalEntry { 302 return createObjectChange{ 303 account: ch.account, 304 } 305 } 306 307 func (ch createContractChange) revert(s *StateDB) { 308 s.getStateObject(ch.account).newContract = false 309 } 310 311 func (ch createContractChange) dirtied() *common.Address { 312 return nil 313 } 314 315 func (ch createContractChange) copy() journalEntry { 316 return createContractChange{ 317 account: ch.account, 318 } 319 } 320 321 func (ch selfDestructChange) revert(s *StateDB) { 322 obj := s.getStateObject(ch.account) 323 if obj != nil { 324 obj.selfDestructed = false 325 } 326 } 327 328 func (ch selfDestructChange) dirtied() *common.Address { 329 return &ch.account 330 } 331 332 func (ch selfDestructChange) copy() journalEntry { 333 return selfDestructChange{ 334 account: ch.account, 335 } 336 } 337 338 var ripemd = common.HexToAddress("0000000000000000000000000000000000000003") 339 340 func (ch touchChange) revert(s *StateDB) { 341 } 342 343 func (ch touchChange) dirtied() *common.Address { 344 return &ch.account 345 } 346 347 func (ch touchChange) copy() journalEntry { 348 return touchChange{ 349 account: ch.account, 350 } 351 } 352 353 func (ch balanceChange) revert(s *StateDB) { 354 s.getStateObject(ch.account).setBalance(ch.prev) 355 } 356 357 func (ch balanceChange) dirtied() *common.Address { 358 return &ch.account 359 } 360 361 func (ch balanceChange) copy() journalEntry { 362 return balanceChange{ 363 account: ch.account, 364 prev: new(uint256.Int).Set(ch.prev), 365 } 366 } 367 368 func (ch nonceChange) revert(s *StateDB) { 369 s.getStateObject(ch.account).setNonce(ch.prev) 370 } 371 372 func (ch nonceChange) dirtied() *common.Address { 373 return &ch.account 374 } 375 376 func (ch nonceChange) copy() journalEntry { 377 return nonceChange{ 378 account: ch.account, 379 prev: ch.prev, 380 } 381 } 382 383 func (ch codeChange) revert(s *StateDB) { 384 s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode) 385 } 386 387 func (ch codeChange) dirtied() *common.Address { 388 return &ch.account 389 } 390 391 func (ch codeChange) copy() journalEntry { 392 return codeChange{ 393 account: ch.account, 394 prevCode: ch.prevCode, 395 } 396 } 397 398 func (ch storageChange) revert(s *StateDB) { 399 s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue) 400 } 401 402 func (ch storageChange) dirtied() *common.Address { 403 return &ch.account 404 } 405 406 func (ch storageChange) copy() journalEntry { 407 return storageChange{ 408 account: ch.account, 409 key: ch.key, 410 prevvalue: ch.prevvalue, 411 origvalue: ch.origvalue, 412 } 413 } 414 415 func (ch transientStorageChange) revert(s *StateDB) { 416 s.setTransientState(ch.account, ch.key, ch.prevalue) 417 } 418 419 func (ch transientStorageChange) dirtied() *common.Address { 420 return nil 421 } 422 423 func (ch transientStorageChange) copy() journalEntry { 424 return transientStorageChange{ 425 account: ch.account, 426 key: ch.key, 427 prevalue: ch.prevalue, 428 } 429 } 430 431 func (ch refundChange) revert(s *StateDB) { 432 s.refund = ch.prev 433 } 434 435 func (ch refundChange) dirtied() *common.Address { 436 return nil 437 } 438 439 func (ch refundChange) copy() journalEntry { 440 return refundChange{ 441 prev: ch.prev, 442 } 443 } 444 445 func (ch addLogChange) revert(s *StateDB) { 446 logs := s.logs[ch.txhash] 447 if len(logs) == 1 { 448 delete(s.logs, ch.txhash) 449 } else { 450 s.logs[ch.txhash] = logs[:len(logs)-1] 451 } 452 s.logSize-- 453 } 454 455 func (ch addLogChange) dirtied() *common.Address { 456 return nil 457 } 458 459 func (ch addLogChange) copy() journalEntry { 460 return addLogChange{ 461 txhash: ch.txhash, 462 } 463 } 464 465 func (ch accessListAddAccountChange) revert(s *StateDB) { 466 /* 467 One important invariant here, is that whenever a (addr, slot) is added, if the 468 addr is not already present, the add causes two journal entries: 469 - one for the address, 470 - one for the (address,slot) 471 Therefore, when unrolling the change, we can always blindly delete the 472 (addr) at this point, since no storage adds can remain when come upon 473 a single (addr) change. 474 */ 475 s.accessList.DeleteAddress(ch.address) 476 } 477 478 func (ch accessListAddAccountChange) dirtied() *common.Address { 479 return nil 480 } 481 482 func (ch accessListAddAccountChange) copy() journalEntry { 483 return accessListAddAccountChange{ 484 address: ch.address, 485 } 486 } 487 488 func (ch accessListAddSlotChange) revert(s *StateDB) { 489 s.accessList.DeleteSlot(ch.address, ch.slot) 490 } 491 492 func (ch accessListAddSlotChange) dirtied() *common.Address { 493 return nil 494 } 495 496 func (ch accessListAddSlotChange) copy() journalEntry { 497 return accessListAddSlotChange{ 498 address: ch.address, 499 slot: ch.slot, 500 } 501 }