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