github.com/klaytn/klaytn@v1.12.1/blockchain/vm/access_list_tracer.go (about) 1 // Modifications Copyright 2023 The klaytn Authors 2 // Copyright 2021 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 18 // This file is derived from eth/tracers/logger/access_list_tracer.go (2023/11/10). 19 // Modified and improved for the klaytn development. 20 21 package vm 22 23 import ( 24 "math/big" 25 26 "github.com/klaytn/klaytn/blockchain/types" 27 "github.com/klaytn/klaytn/common" 28 ) 29 30 // accessList is an accumulator for the set of accounts and storage slots an EVM 31 // contract execution touches. 32 type accessList map[common.Address]accessListSlots 33 34 // accessListSlots is an accumulator for the set of storage slots within a single 35 // contract that an EVM contract execution touches. 36 type accessListSlots map[common.Hash]struct{} 37 38 // newAccessList creates a new accessList. 39 func newAccessList() accessList { 40 return make(map[common.Address]accessListSlots) 41 } 42 43 // addAddress adds an address to the accesslist. 44 func (al accessList) addAddress(address common.Address) { 45 // Set address if not previously present 46 if _, present := al[address]; !present { 47 al[address] = make(map[common.Hash]struct{}) 48 } 49 } 50 51 // addSlot adds a storage slot to the accesslist. 52 func (al accessList) addSlot(address common.Address, slot common.Hash) { 53 // Set address if not previously present 54 al.addAddress(address) 55 56 // Set the slot on the surely existent storage set 57 al[address][slot] = struct{}{} 58 } 59 60 // equal checks if the content of the current access list is the same as the 61 // content of the other one. 62 func (al accessList) equal(other accessList) bool { 63 // Cross reference the accounts first 64 if len(al) != len(other) { 65 return false 66 } 67 // Given that len(al) == len(other), we only need to check that 68 // all the items from al are in other. 69 for addr := range al { 70 if _, ok := other[addr]; !ok { 71 return false 72 } 73 } 74 75 // Accounts match, cross reference the storage slots too 76 for addr, slots := range al { 77 otherslots := other[addr] 78 79 if len(slots) != len(otherslots) { 80 return false 81 } 82 // Given that len(slots) == len(otherslots), we only need to check that 83 // all the items from slots are in otherslots. 84 for hash := range slots { 85 if _, ok := otherslots[hash]; !ok { 86 return false 87 } 88 } 89 } 90 return true 91 } 92 93 // accesslist converts the accesslist to a types.AccessList. 94 func (al accessList) accessList() types.AccessList { 95 acl := make(types.AccessList, 0, len(al)) 96 for addr, slots := range al { 97 tuple := types.AccessTuple{Address: addr, StorageKeys: []common.Hash{}} 98 for slot := range slots { 99 tuple.StorageKeys = append(tuple.StorageKeys, slot) 100 } 101 acl = append(acl, tuple) 102 } 103 return acl 104 } 105 106 // AccessListTracer is a tracer that accumulates touched accounts and storage 107 // slots into an internal set. 108 type AccessListTracer struct { 109 excl map[common.Address]struct{} // Set of account to exclude from the list 110 list accessList // Set of accounts and storage slots touched 111 } 112 113 // NewAccessListTracer creates a new tracer that can generate AccessLists. 114 // An optional AccessList can be specified to occupy slots and addresses in 115 // the resulting accesslist. 116 func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer { 117 excl := map[common.Address]struct{}{ 118 from: {}, to: {}, 119 } 120 for _, addr := range precompiles { 121 excl[addr] = struct{}{} 122 } 123 list := newAccessList() 124 for _, al := range acl { 125 if _, ok := excl[al.Address]; !ok { 126 list.addAddress(al.Address) 127 } 128 for _, slot := range al.StorageKeys { 129 list.addSlot(al.Address, slot) 130 } 131 } 132 return &AccessListTracer{ 133 excl: excl, 134 list: list, 135 } 136 } 137 138 func (a *AccessListTracer) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { 139 } 140 141 // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist. 142 func (a *AccessListTracer) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { 143 stack := scope.Stack 144 stackData := stack.Data() 145 stackLen := len(stackData) 146 if (op == SLOAD || op == SSTORE) && stackLen >= 1 { 147 slot := common.Hash(stackData[stackLen-1].Bytes32()) 148 a.list.addSlot(scope.Contract.Address(), slot) 149 } 150 if (op == EXTCODECOPY || op == EXTCODEHASH || op == EXTCODESIZE || op == BALANCE || op == SELFDESTRUCT) && stackLen >= 1 { 151 addr := common.Address(stackData[stackLen-1].Bytes20()) 152 if _, ok := a.excl[addr]; !ok { 153 a.list.addAddress(addr) 154 } 155 } 156 if (op == DELEGATECALL || op == CALL || op == STATICCALL || op == CALLCODE) && stackLen >= 5 { 157 addr := common.Address(stackData[stackLen-2].Bytes20()) 158 if _, ok := a.excl[addr]; !ok { 159 a.list.addAddress(addr) 160 } 161 } 162 } 163 164 func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) { 165 } 166 167 func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} 168 169 func (*AccessListTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { 170 } 171 172 func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} 173 174 func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {} 175 176 func (*AccessListTracer) CaptureTxEnd(restGas uint64) {} 177 178 // AccessList returns the current accesslist maintained by the tracer. 179 func (a *AccessListTracer) AccessList() types.AccessList { 180 return a.list.accessList() 181 } 182 183 // Equal returns if the content of two access list traces are equal. 184 func (a *AccessListTracer) Equal(other *AccessListTracer) bool { 185 return a.list.equal(other.list) 186 }