github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/eth/tracers/logger/access_list_tracer.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 logger
    18  
    19  import (
    20  	"math/big"
    21  	"time"
    22  
    23  	"github.com/ethw3/go-ethereuma/common"
    24  	"github.com/ethw3/go-ethereuma/core/types"
    25  	"github.com/ethw3/go-ethereuma/core/vm"
    26  )
    27  
    28  // accessList is an accumulator for the set of accounts and storage slots an EVM
    29  // contract execution touches.
    30  type accessList map[common.Address]accessListSlots
    31  
    32  // accessListSlots is an accumulator for the set of storage slots within a single
    33  // contract that an EVM contract execution touches.
    34  type accessListSlots map[common.Hash]struct{}
    35  
    36  // newAccessList creates a new accessList.
    37  func newAccessList() accessList {
    38  	return make(map[common.Address]accessListSlots)
    39  }
    40  
    41  // addAddress adds an address to the accesslist.
    42  func (al accessList) addAddress(address common.Address) {
    43  	// Set address if not previously present
    44  	if _, present := al[address]; !present {
    45  		al[address] = make(map[common.Hash]struct{})
    46  	}
    47  }
    48  
    49  // addSlot adds a storage slot to the accesslist.
    50  func (al accessList) addSlot(address common.Address, slot common.Hash) {
    51  	// Set address if not previously present
    52  	al.addAddress(address)
    53  
    54  	// Set the slot on the surely existent storage set
    55  	al[address][slot] = struct{}{}
    56  }
    57  
    58  // equal checks if the content of the current access list is the same as the
    59  // content of the other one.
    60  func (al accessList) equal(other accessList) bool {
    61  	// Cross reference the accounts first
    62  	if len(al) != len(other) {
    63  		return false
    64  	}
    65  	// Given that len(al) == len(other), we only need to check that
    66  	// all the items from al are in other.
    67  	for addr := range al {
    68  		if _, ok := other[addr]; !ok {
    69  			return false
    70  		}
    71  	}
    72  
    73  	// Accounts match, cross reference the storage slots too
    74  	for addr, slots := range al {
    75  		otherslots := other[addr]
    76  
    77  		if len(slots) != len(otherslots) {
    78  			return false
    79  		}
    80  		// Given that len(slots) == len(otherslots), we only need to check that
    81  		// all the items from slots are in otherslots.
    82  		for hash := range slots {
    83  			if _, ok := otherslots[hash]; !ok {
    84  				return false
    85  			}
    86  		}
    87  	}
    88  	return true
    89  }
    90  
    91  // accesslist converts the accesslist to a types.AccessList.
    92  func (al accessList) accessList() types.AccessList {
    93  	acl := make(types.AccessList, 0, len(al))
    94  	for addr, slots := range al {
    95  		tuple := types.AccessTuple{Address: addr, StorageKeys: []common.Hash{}}
    96  		for slot := range slots {
    97  			tuple.StorageKeys = append(tuple.StorageKeys, slot)
    98  		}
    99  		acl = append(acl, tuple)
   100  	}
   101  	return acl
   102  }
   103  
   104  // AccessListTracer is a tracer that accumulates touched accounts and storage
   105  // slots into an internal set.
   106  type AccessListTracer struct {
   107  	excl map[common.Address]struct{} // Set of account to exclude from the list
   108  	list accessList                  // Set of accounts and storage slots touched
   109  }
   110  
   111  // NewAccessListTracer creates a new tracer that can generate AccessLists.
   112  // An optional AccessList can be specified to occupy slots and addresses in
   113  // the resulting accesslist.
   114  func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompiles []common.Address) *AccessListTracer {
   115  	excl := map[common.Address]struct{}{
   116  		from: {}, to: {},
   117  	}
   118  	for _, addr := range precompiles {
   119  		excl[addr] = struct{}{}
   120  	}
   121  	list := newAccessList()
   122  	for _, al := range acl {
   123  		if _, ok := excl[al.Address]; !ok {
   124  			list.addAddress(al.Address)
   125  		}
   126  		for _, slot := range al.StorageKeys {
   127  			list.addSlot(al.Address, slot)
   128  		}
   129  	}
   130  	return &AccessListTracer{
   131  		excl: excl,
   132  		list: list,
   133  	}
   134  }
   135  
   136  func (a *AccessListTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
   137  }
   138  
   139  // CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
   140  func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
   141  	stack := scope.Stack
   142  	stackData := stack.Data()
   143  	stackLen := len(stackData)
   144  	if (op == vm.SLOAD || op == vm.SSTORE) && stackLen >= 1 {
   145  		slot := common.Hash(stackData[stackLen-1].Bytes32())
   146  		a.list.addSlot(scope.Contract.Address(), slot)
   147  	}
   148  	if (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT) && stackLen >= 1 {
   149  		addr := common.Address(stackData[stackLen-1].Bytes20())
   150  		if _, ok := a.excl[addr]; !ok {
   151  			a.list.addAddress(addr)
   152  		}
   153  	}
   154  	if (op == vm.DELEGATECALL || op == vm.CALL || op == vm.STATICCALL || op == vm.CALLCODE) && stackLen >= 5 {
   155  		addr := common.Address(stackData[stackLen-2].Bytes20())
   156  		if _, ok := a.excl[addr]; !ok {
   157  			a.list.addAddress(addr)
   158  		}
   159  	}
   160  }
   161  
   162  func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
   163  }
   164  
   165  func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
   166  
   167  func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
   168  }
   169  
   170  func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
   171  
   172  func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {}
   173  
   174  func (*AccessListTracer) CaptureTxEnd(restGas uint64) {}
   175  
   176  // AccessList returns the current accesslist maintained by the tracer.
   177  func (a *AccessListTracer) AccessList() types.AccessList {
   178  	return a.list.accessList()
   179  }
   180  
   181  // Equal returns if the content of two access list traces are equal.
   182  func (a *AccessListTracer) Equal(other *AccessListTracer) bool {
   183  	return a.list.equal(other.list)
   184  }