github.com/amazechain/amc@v0.1.3/internal/tracers/logger/access_list_tracer.go (about)

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