github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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  	"maps"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/holiman/uint256"
    24  )
    25  
    26  // journalEntry is a modification entry in the state change journal that can be
    27  // reverted on demand.
    28  type journalEntry interface {
    29  	// revert undoes the changes introduced by this journal entry.
    30  	revert(*StateDB)
    31  
    32  	// dirtied returns the Ethereum address modified by this journal entry.
    33  	dirtied() *common.Address
    34  
    35  	// copy returns a deep-copied journal entry.
    36  	copy() journalEntry
    37  }
    38  
    39  // journal contains the list of state modifications applied since the last state
    40  // commit. These are tracked to be able to be reverted in the case of an execution
    41  // exception or request for reversal.
    42  type journal struct {
    43  	entries []journalEntry         // Current changes tracked by the journal
    44  	dirties map[common.Address]int // Dirty accounts and the number of changes
    45  }
    46  
    47  // newJournal creates a new initialized journal.
    48  func newJournal() *journal {
    49  	return &journal{
    50  		dirties: make(map[common.Address]int),
    51  	}
    52  }
    53  
    54  // append inserts a new modification entry to the end of the change journal.
    55  func (j *journal) append(entry journalEntry) {
    56  	j.entries = append(j.entries, entry)
    57  	if addr := entry.dirtied(); addr != nil {
    58  		j.dirties[*addr]++
    59  	}
    60  }
    61  
    62  // revert undoes a batch of journalled modifications along with any reverted
    63  // dirty handling too.
    64  func (j *journal) revert(statedb *StateDB, snapshot int) {
    65  	for i := len(j.entries) - 1; i >= snapshot; i-- {
    66  		// Undo the changes made by the operation
    67  		j.entries[i].revert(statedb)
    68  
    69  		// Drop any dirty tracking induced by the change
    70  		if addr := j.entries[i].dirtied(); addr != nil {
    71  			if j.dirties[*addr]--; j.dirties[*addr] == 0 {
    72  				delete(j.dirties, *addr)
    73  			}
    74  		}
    75  	}
    76  	j.entries = j.entries[:snapshot]
    77  }
    78  
    79  // dirty explicitly sets an address to dirty, even if the change entries would
    80  // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
    81  // precompile consensus exception.
    82  func (j *journal) dirty(addr common.Address) {
    83  	j.dirties[addr]++
    84  }
    85  
    86  // length returns the current number of entries in the journal.
    87  func (j *journal) length() int {
    88  	return len(j.entries)
    89  }
    90  
    91  // copy returns a deep-copied journal.
    92  func (j *journal) copy() *journal {
    93  	entries := make([]journalEntry, 0, j.length())
    94  	for i := 0; i < j.length(); i++ {
    95  		entries = append(entries, j.entries[i].copy())
    96  	}
    97  	return &journal{
    98  		entries: entries,
    99  		dirties: maps.Clone(j.dirties),
   100  	}
   101  }
   102  
   103  type (
   104  	// Changes to the account trie.
   105  	createObjectChange struct {
   106  		account *common.Address
   107  	}
   108  
   109  	// createContractChange represents an account becoming a contract-account.
   110  	// This event happens prior to executing initcode. The journal-event simply
   111  	// manages the created-flag, in order to allow same-tx destruction.
   112  	createContractChange struct {
   113  		account common.Address
   114  	}
   115  
   116  	selfDestructChange struct {
   117  		account     *common.Address
   118  		prev        bool // whether account had already self-destructed
   119  		prevbalance *uint256.Int
   120  	}
   121  
   122  	// Changes to individual accounts.
   123  	balanceChange struct {
   124  		account *common.Address
   125  		prev    *uint256.Int
   126  	}
   127  	nonceChange struct {
   128  		account *common.Address
   129  		prev    uint64
   130  	}
   131  	storageChange struct {
   132  		account   *common.Address
   133  		key       common.Hash
   134  		prevvalue common.Hash
   135  		origvalue common.Hash
   136  	}
   137  	codeChange struct {
   138  		account            *common.Address
   139  		prevcode, prevhash []byte
   140  	}
   141  
   142  	// Changes to other state values.
   143  	refundChange struct {
   144  		prev uint64
   145  	}
   146  	addLogChange struct {
   147  		txhash common.Hash
   148  	}
   149  	addPreimageChange struct {
   150  		hash common.Hash
   151  	}
   152  	touchChange struct {
   153  		account *common.Address
   154  	}
   155  
   156  	// Changes to the access list
   157  	accessListAddAccountChange struct {
   158  		address *common.Address
   159  	}
   160  	accessListAddSlotChange struct {
   161  		address *common.Address
   162  		slot    *common.Hash
   163  	}
   164  
   165  	// Changes to transient storage
   166  	transientStorageChange struct {
   167  		account       *common.Address
   168  		key, prevalue common.Hash
   169  	}
   170  )
   171  
   172  func (ch createObjectChange) revert(s *StateDB) {
   173  	delete(s.stateObjects, *ch.account)
   174  }
   175  
   176  func (ch createObjectChange) dirtied() *common.Address {
   177  	return ch.account
   178  }
   179  
   180  func (ch createObjectChange) copy() journalEntry {
   181  	return createObjectChange{
   182  		account: ch.account,
   183  	}
   184  }
   185  
   186  func (ch createContractChange) revert(s *StateDB) {
   187  	s.getStateObject(ch.account).newContract = false
   188  }
   189  
   190  func (ch createContractChange) dirtied() *common.Address {
   191  	return nil
   192  }
   193  
   194  func (ch createContractChange) copy() journalEntry {
   195  	return createContractChange{
   196  		account: ch.account,
   197  	}
   198  }
   199  
   200  func (ch selfDestructChange) revert(s *StateDB) {
   201  	obj := s.getStateObject(*ch.account)
   202  	if obj != nil {
   203  		obj.selfDestructed = ch.prev
   204  		obj.setBalance(ch.prevbalance)
   205  	}
   206  }
   207  
   208  func (ch selfDestructChange) dirtied() *common.Address {
   209  	return ch.account
   210  }
   211  
   212  func (ch selfDestructChange) copy() journalEntry {
   213  	return selfDestructChange{
   214  		account:     ch.account,
   215  		prev:        ch.prev,
   216  		prevbalance: new(uint256.Int).Set(ch.prevbalance),
   217  	}
   218  }
   219  
   220  var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
   221  
   222  func (ch touchChange) revert(s *StateDB) {
   223  }
   224  
   225  func (ch touchChange) dirtied() *common.Address {
   226  	return ch.account
   227  }
   228  
   229  func (ch touchChange) copy() journalEntry {
   230  	return touchChange{
   231  		account: ch.account,
   232  	}
   233  }
   234  
   235  func (ch balanceChange) revert(s *StateDB) {
   236  	s.getStateObject(*ch.account).setBalance(ch.prev)
   237  }
   238  
   239  func (ch balanceChange) dirtied() *common.Address {
   240  	return ch.account
   241  }
   242  
   243  func (ch balanceChange) copy() journalEntry {
   244  	return balanceChange{
   245  		account: ch.account,
   246  		prev:    new(uint256.Int).Set(ch.prev),
   247  	}
   248  }
   249  
   250  func (ch nonceChange) revert(s *StateDB) {
   251  	s.getStateObject(*ch.account).setNonce(ch.prev)
   252  }
   253  
   254  func (ch nonceChange) dirtied() *common.Address {
   255  	return ch.account
   256  }
   257  
   258  func (ch nonceChange) copy() journalEntry {
   259  	return nonceChange{
   260  		account: ch.account,
   261  		prev:    ch.prev,
   262  	}
   263  }
   264  
   265  func (ch codeChange) revert(s *StateDB) {
   266  	s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
   267  }
   268  
   269  func (ch codeChange) dirtied() *common.Address {
   270  	return ch.account
   271  }
   272  
   273  func (ch codeChange) copy() journalEntry {
   274  	return codeChange{
   275  		account:  ch.account,
   276  		prevhash: common.CopyBytes(ch.prevhash),
   277  		prevcode: common.CopyBytes(ch.prevcode),
   278  	}
   279  }
   280  
   281  func (ch storageChange) revert(s *StateDB) {
   282  	s.getStateObject(*ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
   283  }
   284  
   285  func (ch storageChange) dirtied() *common.Address {
   286  	return ch.account
   287  }
   288  
   289  func (ch storageChange) copy() journalEntry {
   290  	return storageChange{
   291  		account:   ch.account,
   292  		key:       ch.key,
   293  		prevvalue: ch.prevvalue,
   294  	}
   295  }
   296  
   297  func (ch transientStorageChange) revert(s *StateDB) {
   298  	s.setTransientState(*ch.account, ch.key, ch.prevalue)
   299  }
   300  
   301  func (ch transientStorageChange) dirtied() *common.Address {
   302  	return nil
   303  }
   304  
   305  func (ch transientStorageChange) copy() journalEntry {
   306  	return transientStorageChange{
   307  		account:  ch.account,
   308  		key:      ch.key,
   309  		prevalue: ch.prevalue,
   310  	}
   311  }
   312  
   313  func (ch refundChange) revert(s *StateDB) {
   314  	s.refund = ch.prev
   315  }
   316  
   317  func (ch refundChange) dirtied() *common.Address {
   318  	return nil
   319  }
   320  
   321  func (ch refundChange) copy() journalEntry {
   322  	return refundChange{
   323  		prev: ch.prev,
   324  	}
   325  }
   326  
   327  func (ch addLogChange) revert(s *StateDB) {
   328  	logs := s.logs[ch.txhash]
   329  	if len(logs) == 1 {
   330  		delete(s.logs, ch.txhash)
   331  	} else {
   332  		s.logs[ch.txhash] = logs[:len(logs)-1]
   333  	}
   334  	s.logSize--
   335  }
   336  
   337  func (ch addLogChange) dirtied() *common.Address {
   338  	return nil
   339  }
   340  
   341  func (ch addLogChange) copy() journalEntry {
   342  	return addLogChange{
   343  		txhash: ch.txhash,
   344  	}
   345  }
   346  
   347  func (ch addPreimageChange) revert(s *StateDB) {
   348  	delete(s.preimages, ch.hash)
   349  }
   350  
   351  func (ch addPreimageChange) dirtied() *common.Address {
   352  	return nil
   353  }
   354  
   355  func (ch addPreimageChange) copy() journalEntry {
   356  	return addPreimageChange{
   357  		hash: ch.hash,
   358  	}
   359  }
   360  
   361  func (ch accessListAddAccountChange) revert(s *StateDB) {
   362  	/*
   363  		One important invariant here, is that whenever a (addr, slot) is added, if the
   364  		addr is not already present, the add causes two journal entries:
   365  		- one for the address,
   366  		- one for the (address,slot)
   367  		Therefore, when unrolling the change, we can always blindly delete the
   368  		(addr) at this point, since no storage adds can remain when come upon
   369  		a single (addr) change.
   370  	*/
   371  	s.accessList.DeleteAddress(*ch.address)
   372  }
   373  
   374  func (ch accessListAddAccountChange) dirtied() *common.Address {
   375  	return nil
   376  }
   377  
   378  func (ch accessListAddAccountChange) copy() journalEntry {
   379  	return accessListAddAccountChange{
   380  		address: ch.address,
   381  	}
   382  }
   383  
   384  func (ch accessListAddSlotChange) revert(s *StateDB) {
   385  	s.accessList.DeleteSlot(*ch.address, *ch.slot)
   386  }
   387  
   388  func (ch accessListAddSlotChange) dirtied() *common.Address {
   389  	return nil
   390  }
   391  
   392  func (ch accessListAddSlotChange) copy() journalEntry {
   393  	return accessListAddSlotChange{
   394  		address: ch.address,
   395  		slot:    ch.slot,
   396  	}
   397  }