github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/core/state/access_list.go (about) 1 // Copyright 2020 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 "fmt" 21 "maps" 22 "slices" 23 "strings" 24 25 "github.com/ethereum/go-ethereum/common" 26 ) 27 28 type accessList struct { 29 addresses map[common.Address]int 30 slots []map[common.Hash]struct{} 31 } 32 33 // ContainsAddress returns true if the address is in the access list. 34 func (al *accessList) ContainsAddress(address common.Address) bool { 35 _, ok := al.addresses[address] 36 return ok 37 } 38 39 // Contains checks if a slot within an account is present in the access list, returning 40 // separate flags for the presence of the account and the slot respectively. 41 func (al *accessList) Contains(address common.Address, slot common.Hash) (addressPresent bool, slotPresent bool) { 42 idx, ok := al.addresses[address] 43 if !ok { 44 // no such address (and hence zero slots) 45 return false, false 46 } 47 if idx == -1 { 48 // address yes, but no slots 49 return true, false 50 } 51 _, slotPresent = al.slots[idx][slot] 52 return true, slotPresent 53 } 54 55 // newAccessList creates a new accessList. 56 func newAccessList() *accessList { 57 return &accessList{ 58 addresses: make(map[common.Address]int), 59 } 60 } 61 62 // Copy creates an independent copy of an accessList. 63 func (a *accessList) Copy() *accessList { 64 cp := newAccessList() 65 cp.addresses = maps.Clone(a.addresses) 66 cp.slots = make([]map[common.Hash]struct{}, len(a.slots)) 67 for i, slotMap := range a.slots { 68 cp.slots[i] = maps.Clone(slotMap) 69 } 70 return cp 71 } 72 73 // AddAddress adds an address to the access list, and returns 'true' if the operation 74 // caused a change (addr was not previously in the list). 75 func (al *accessList) AddAddress(address common.Address) bool { 76 if _, present := al.addresses[address]; present { 77 return false 78 } 79 al.addresses[address] = -1 80 return true 81 } 82 83 // AddSlot adds the specified (addr, slot) combo to the access list. 84 // Return values are: 85 // - address added 86 // - slot added 87 // For any 'true' value returned, a corresponding journal entry must be made. 88 func (al *accessList) AddSlot(address common.Address, slot common.Hash) (addrChange bool, slotChange bool) { 89 idx, addrPresent := al.addresses[address] 90 if !addrPresent || idx == -1 { 91 // Address not present, or addr present but no slots there 92 al.addresses[address] = len(al.slots) 93 slotmap := map[common.Hash]struct{}{slot: {}} 94 al.slots = append(al.slots, slotmap) 95 return !addrPresent, true 96 } 97 // There is already an (address,slot) mapping 98 slotmap := al.slots[idx] 99 if _, ok := slotmap[slot]; !ok { 100 slotmap[slot] = struct{}{} 101 // Journal add slot change 102 return false, true 103 } 104 // No changes required 105 return false, false 106 } 107 108 // DeleteSlot removes an (address, slot)-tuple from the access list. 109 // This operation needs to be performed in the same order as the addition happened. 110 // This method is meant to be used by the journal, which maintains ordering of 111 // operations. 112 func (al *accessList) DeleteSlot(address common.Address, slot common.Hash) { 113 idx, addrOk := al.addresses[address] 114 // There are two ways this can fail 115 if !addrOk { 116 panic("reverting slot change, address not present in list") 117 } 118 slotmap := al.slots[idx] 119 delete(slotmap, slot) 120 // If that was the last (first) slot, remove it 121 // Since additions and rollbacks are always performed in order, 122 // we can delete the item without worrying about screwing up later indices 123 if len(slotmap) == 0 { 124 al.slots = al.slots[:idx] 125 al.addresses[address] = -1 126 } 127 } 128 129 // DeleteAddress removes an address from the access list. This operation 130 // needs to be performed in the same order as the addition happened. 131 // This method is meant to be used by the journal, which maintains ordering of 132 // operations. 133 func (al *accessList) DeleteAddress(address common.Address) { 134 delete(al.addresses, address) 135 } 136 137 // Equal returns true if the two access lists are identical 138 func (al *accessList) Equal(other *accessList) bool { 139 if !maps.Equal(al.addresses, other.addresses) { 140 return false 141 } 142 return slices.EqualFunc(al.slots, other.slots, 143 func(m map[common.Hash]struct{}, m2 map[common.Hash]struct{}) bool { 144 return maps.Equal(m, m2) 145 }) 146 } 147 148 // PrettyPrint prints the contents of the access list in a human-readable form 149 func (al *accessList) PrettyPrint() string { 150 out := new(strings.Builder) 151 var sortedAddrs []common.Address 152 for addr := range al.addresses { 153 sortedAddrs = append(sortedAddrs, addr) 154 } 155 slices.SortFunc(sortedAddrs, common.Address.Cmp) 156 for _, addr := range sortedAddrs { 157 idx := al.addresses[addr] 158 fmt.Fprintf(out, "%#x : (idx %d)\n", addr, idx) 159 if idx >= 0 { 160 slotmap := al.slots[idx] 161 for h := range slotmap { 162 fmt.Fprintf(out, " %#x\n", h) 163 } 164 } 165 } 166 return out.String() 167 }