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 }