github.com/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  }