github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/core/state/access_events.go (about)

     1  // Copyright 2021 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/ethereum/go-ethereum/common/math"
    24  	"github.com/ethereum/go-ethereum/params"
    25  	"github.com/ethereum/go-ethereum/trie/utils"
    26  	"github.com/holiman/uint256"
    27  )
    28  
    29  // mode specifies how a tree location has been accessed
    30  // for the byte value:
    31  // * the first bit is set if the branch has been edited
    32  // * the second bit is set if the branch has been read
    33  type mode byte
    34  
    35  const (
    36  	AccessWitnessReadFlag  = mode(1)
    37  	AccessWitnessWriteFlag = mode(2)
    38  )
    39  
    40  var zeroTreeIndex uint256.Int
    41  
    42  // AccessEvents lists the locations of the state that are being accessed
    43  // during the production of a block.
    44  type AccessEvents struct {
    45  	branches map[branchAccessKey]mode
    46  	chunks   map[chunkAccessKey]mode
    47  
    48  	pointCache *utils.PointCache
    49  }
    50  
    51  func NewAccessEvents(pointCache *utils.PointCache) *AccessEvents {
    52  	return &AccessEvents{
    53  		branches:   make(map[branchAccessKey]mode),
    54  		chunks:     make(map[chunkAccessKey]mode),
    55  		pointCache: pointCache,
    56  	}
    57  }
    58  
    59  // Merge is used to merge the access events that were generated during the
    60  // execution of a tx, with the accumulation of all access events that were
    61  // generated during the execution of all txs preceding this one in a block.
    62  func (ae *AccessEvents) Merge(other *AccessEvents) {
    63  	for k := range other.branches {
    64  		ae.branches[k] |= other.branches[k]
    65  	}
    66  	for k, chunk := range other.chunks {
    67  		ae.chunks[k] |= chunk
    68  	}
    69  }
    70  
    71  // Keys returns, predictably, the list of keys that were touched during the
    72  // buildup of the access witness.
    73  func (ae *AccessEvents) Keys() [][]byte {
    74  	// TODO: consider if parallelizing this is worth it, probably depending on len(ae.chunks).
    75  	keys := make([][]byte, 0, len(ae.chunks))
    76  	for chunk := range ae.chunks {
    77  		basePoint := ae.pointCache.Get(chunk.addr[:])
    78  		key := utils.GetTreeKeyWithEvaluatedAddress(basePoint, &chunk.treeIndex, chunk.leafKey)
    79  		keys = append(keys, key)
    80  	}
    81  	return keys
    82  }
    83  
    84  func (ae *AccessEvents) Copy() *AccessEvents {
    85  	cpy := &AccessEvents{
    86  		branches:   maps.Clone(ae.branches),
    87  		chunks:     maps.Clone(ae.chunks),
    88  		pointCache: ae.pointCache,
    89  	}
    90  	return cpy
    91  }
    92  
    93  // AddAccount returns the gas to be charged for each of the currently cold
    94  // member fields of an account.
    95  func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
    96  	var gas uint64
    97  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
    98  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
    99  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
   100  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
   101  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
   102  	return gas
   103  }
   104  
   105  // MessageCallGas returns the gas to be charged for each of the currently
   106  // cold member fields of an account, that need to be touched when making a message
   107  // call to that account.
   108  func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
   109  	var gas uint64
   110  	gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false)
   111  	gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false)
   112  	return gas
   113  }
   114  
   115  // ValueTransferGas returns the gas to be charged for each of the currently
   116  // cold balance member fields of the caller and the callee accounts.
   117  func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
   118  	var gas uint64
   119  	gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
   120  	gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
   121  	return gas
   122  }
   123  
   124  // ContractCreateInitGas returns the access gas costs for the initialization of
   125  // a contract creation.
   126  func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
   127  	var gas uint64
   128  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
   129  	gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
   130  	if createSendsValue {
   131  		gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
   132  	}
   133  	return gas
   134  }
   135  
   136  // AddTxOrigin adds the member fields of the sender account to the access event list,
   137  // so that cold accesses are not charged, since they are covered by the 21000 gas.
   138  func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
   139  	ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false)
   140  	ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
   141  	ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true)
   142  	ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
   143  	ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
   144  }
   145  
   146  // AddTxDestination adds the member fields of the sender account to the access event list,
   147  // so that cold accesses are not charged, since they are covered by the 21000 gas.
   148  func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
   149  	ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
   150  	ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue)
   151  	ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false)
   152  	ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
   153  	ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
   154  }
   155  
   156  // SlotGas returns the amount of gas to be charged for a cold storage access.
   157  func (ae *AccessEvents) SlotGas(addr common.Address, slot common.Hash, isWrite bool) uint64 {
   158  	treeIndex, subIndex := utils.StorageIndex(slot.Bytes())
   159  	return ae.touchAddressAndChargeGas(addr, *treeIndex, subIndex, isWrite)
   160  }
   161  
   162  // touchAddressAndChargeGas adds any missing access event to the access event list, and returns the cold
   163  // access cost to be charged, if need be.
   164  func (ae *AccessEvents) touchAddressAndChargeGas(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) uint64 {
   165  	stemRead, selectorRead, stemWrite, selectorWrite, selectorFill := ae.touchAddress(addr, treeIndex, subIndex, isWrite)
   166  
   167  	var gas uint64
   168  	if stemRead {
   169  		gas += params.WitnessBranchReadCost
   170  	}
   171  	if selectorRead {
   172  		gas += params.WitnessChunkReadCost
   173  	}
   174  	if stemWrite {
   175  		gas += params.WitnessBranchWriteCost
   176  	}
   177  	if selectorWrite {
   178  		gas += params.WitnessChunkWriteCost
   179  	}
   180  	if selectorFill {
   181  		gas += params.WitnessChunkFillCost
   182  	}
   183  	return gas
   184  }
   185  
   186  // touchAddress adds any missing access event to the access event list.
   187  func (ae *AccessEvents) touchAddress(addr common.Address, treeIndex uint256.Int, subIndex byte, isWrite bool) (bool, bool, bool, bool, bool) {
   188  	branchKey := newBranchAccessKey(addr, treeIndex)
   189  	chunkKey := newChunkAccessKey(branchKey, subIndex)
   190  
   191  	// Read access.
   192  	var branchRead, chunkRead bool
   193  	if _, hasStem := ae.branches[branchKey]; !hasStem {
   194  		branchRead = true
   195  		ae.branches[branchKey] = AccessWitnessReadFlag
   196  	}
   197  	if _, hasSelector := ae.chunks[chunkKey]; !hasSelector {
   198  		chunkRead = true
   199  		ae.chunks[chunkKey] = AccessWitnessReadFlag
   200  	}
   201  
   202  	// Write access.
   203  	var branchWrite, chunkWrite, chunkFill bool
   204  	if isWrite {
   205  		if (ae.branches[branchKey] & AccessWitnessWriteFlag) == 0 {
   206  			branchWrite = true
   207  			ae.branches[branchKey] |= AccessWitnessWriteFlag
   208  		}
   209  
   210  		chunkValue := ae.chunks[chunkKey]
   211  		if (chunkValue & AccessWitnessWriteFlag) == 0 {
   212  			chunkWrite = true
   213  			ae.chunks[chunkKey] |= AccessWitnessWriteFlag
   214  		}
   215  		// TODO: charge chunk filling costs if the leaf was previously empty in the state
   216  	}
   217  	return branchRead, chunkRead, branchWrite, chunkWrite, chunkFill
   218  }
   219  
   220  type branchAccessKey struct {
   221  	addr      common.Address
   222  	treeIndex uint256.Int
   223  }
   224  
   225  func newBranchAccessKey(addr common.Address, treeIndex uint256.Int) branchAccessKey {
   226  	var sk branchAccessKey
   227  	sk.addr = addr
   228  	sk.treeIndex = treeIndex
   229  	return sk
   230  }
   231  
   232  type chunkAccessKey struct {
   233  	branchAccessKey
   234  	leafKey byte
   235  }
   236  
   237  func newChunkAccessKey(branchKey branchAccessKey, leafKey byte) chunkAccessKey {
   238  	var lk chunkAccessKey
   239  	lk.branchAccessKey = branchKey
   240  	lk.leafKey = leafKey
   241  	return lk
   242  }
   243  
   244  // CodeChunksRangeGas is a helper function to touch every chunk in a code range and charge witness gas costs
   245  func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC, size uint64, codeLen uint64, isWrite bool) uint64 {
   246  	// note that in the case where the copied code is outside the range of the
   247  	// contract code but touches the last leaf with contract code in it,
   248  	// we don't include the last leaf of code in the AccessWitness.  The
   249  	// reason that we do not need the last leaf is the account's code size
   250  	// is already in the AccessWitness so a stateless verifier can see that
   251  	// the code from the last leaf is not needed.
   252  	if (codeLen == 0 && size == 0) || startPC > codeLen {
   253  		return 0
   254  	}
   255  
   256  	endPC := startPC + size
   257  	if endPC > codeLen {
   258  		endPC = codeLen
   259  	}
   260  	if endPC > 0 {
   261  		endPC -= 1 // endPC is the last bytecode that will be touched.
   262  	}
   263  
   264  	var statelessGasCharged uint64
   265  	for chunkNumber := startPC / 31; chunkNumber <= endPC/31; chunkNumber++ {
   266  		treeIndex := *uint256.NewInt((chunkNumber + 128) / 256)
   267  		subIndex := byte((chunkNumber + 128) % 256)
   268  		gas := ae.touchAddressAndChargeGas(contractAddr, treeIndex, subIndex, isWrite)
   269  		var overflow bool
   270  		statelessGasCharged, overflow = math.SafeAdd(statelessGasCharged, gas)
   271  		if overflow {
   272  			panic("overflow when adding gas")
   273  		}
   274  	}
   275  	return statelessGasCharged
   276  }
   277  
   278  // VersionGas adds the account's version to the accessed data, and returns the
   279  // amount of gas that it costs.
   280  // Note that an access in write mode implies an access in read mode, whereas an
   281  // access in read mode does not imply an access in write mode.
   282  func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 {
   283  	return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
   284  }
   285  
   286  // BalanceGas adds the account's balance to the accessed data, and returns the
   287  // amount of gas that it costs.
   288  // in write mode. If false, the charged gas corresponds to an access in read mode.
   289  // Note that an access in write mode implies an access in read mode, whereas an access in
   290  // read mode does not imply an access in write mode.
   291  func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 {
   292  	return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
   293  }
   294  
   295  // NonceGas adds the account's nonce to the accessed data, and returns the
   296  // amount of gas that it costs.
   297  // in write mode. If false, the charged gas corresponds to an access in read mode.
   298  // Note that an access in write mode implies an access in read mode, whereas an access in
   299  // read mode does not imply an access in write mode.
   300  func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 {
   301  	return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
   302  }
   303  
   304  // CodeSizeGas adds the account's code size to the accessed data, and returns the
   305  // amount of gas that it costs.
   306  // in write mode. If false, the charged gas corresponds to an access in read mode.
   307  // Note that an access in write mode implies an access in read mode, whereas an access in
   308  // read mode does not imply an access in write mode.
   309  func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
   310  	return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
   311  }
   312  
   313  // CodeHashGas adds the account's code hash to the accessed data, and returns the
   314  // amount of gas that it costs.
   315  // in write mode. If false, the charged gas corresponds to an access in read mode.
   316  // Note that an access in write mode implies an access in read mode, whereas an access in
   317  // read mode does not imply an access in write mode.
   318  func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
   319  	return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
   320  }