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  }