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 }