|  | 
|  | 1 | +package core | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"github.com/ethereum/go-ethereum/common" | 
|  | 5 | +	"github.com/ethereum/go-ethereum/core/tracing" | 
|  | 6 | +	"github.com/ethereum/go-ethereum/core/types" | 
|  | 7 | +	"github.com/ethereum/go-ethereum/core/types/bal" | 
|  | 8 | +	"github.com/holiman/uint256" | 
|  | 9 | +	"math/big" | 
|  | 10 | +) | 
|  | 11 | + | 
|  | 12 | +// BlockAccessListTracer constructs an EIP-7928 block access list from the | 
|  | 13 | +// execution of a block | 
|  | 14 | +type BlockAccessListTracer struct { | 
|  | 15 | +	// this is a set of access lists for each call scope. the overall block access lists | 
|  | 16 | +	// is accrued at index 0, while the access lists of various nested execution | 
|  | 17 | +	// scopes are in the proceeding indices. | 
|  | 18 | +	// When an execution scope terminates in a non-reverting fashion, the changes are | 
|  | 19 | +	// merged into the access list of the parent scope. | 
|  | 20 | +	callAccessLists []*bal.ConstructionBlockAccessList | 
|  | 21 | +	txIdx           uint16 | 
|  | 22 | + | 
|  | 23 | +	// if non-nil, it's the address of the account which just self-destructed. | 
|  | 24 | +	// reset at the end of the call-scope which self-destructed. | 
|  | 25 | +	selfdestructedAccount *common.Address | 
|  | 26 | +} | 
|  | 27 | + | 
|  | 28 | +// NewBlockAccessListTracer returns an BlockAccessListTracer and a set of hooks | 
|  | 29 | +func NewBlockAccessListTracer() (*BlockAccessListTracer, *tracing.Hooks) { | 
|  | 30 | +	balTracer := &BlockAccessListTracer{ | 
|  | 31 | +		callAccessLists: []*bal.ConstructionBlockAccessList{bal.NewConstructionBlockAccessList()}, | 
|  | 32 | +		txIdx:           0, | 
|  | 33 | +	} | 
|  | 34 | +	hooks := &tracing.Hooks{ | 
|  | 35 | +		OnTxEnd:           balTracer.TxEndHook, | 
|  | 36 | +		OnTxStart:         balTracer.TxStartHook, | 
|  | 37 | +		OnEnter:           balTracer.OnEnter, | 
|  | 38 | +		OnExit:            balTracer.OnExit, | 
|  | 39 | +		OnCodeChangeV2:    balTracer.OnCodeChange, | 
|  | 40 | +		OnBalanceChange:   balTracer.OnBalanceChange, | 
|  | 41 | +		OnNonceChange:     balTracer.OnNonceChange, | 
|  | 42 | +		OnStorageChange:   balTracer.OnStorageChange, | 
|  | 43 | +		OnColdAccountRead: balTracer.OnColdAccountRead, | 
|  | 44 | +		OnColdStorageRead: balTracer.OnColdStorageRead, | 
|  | 45 | +	} | 
|  | 46 | +	return balTracer, hooks | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +// AccessList returns the constructed access list | 
|  | 50 | +func (a *BlockAccessListTracer) AccessList() *bal.ConstructionBlockAccessList { | 
|  | 51 | +	return a.callAccessLists[0] | 
|  | 52 | +} | 
|  | 53 | + | 
|  | 54 | +func (a *BlockAccessListTracer) TxEndHook(receipt *types.Receipt, err error) { | 
|  | 55 | +	a.txIdx++ | 
|  | 56 | +} | 
|  | 57 | + | 
|  | 58 | +func (a *BlockAccessListTracer) TxStartHook(vm *tracing.VMContext, tx *types.Transaction, from common.Address) { | 
|  | 59 | +	if a.txIdx == 0 { | 
|  | 60 | +		a.txIdx++ | 
|  | 61 | +	} | 
|  | 62 | +} | 
|  | 63 | + | 
|  | 64 | +func (a *BlockAccessListTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { | 
|  | 65 | +	a.callAccessLists = append(a.callAccessLists, bal.NewConstructionBlockAccessList()) | 
|  | 66 | +} | 
|  | 67 | + | 
|  | 68 | +func (a *BlockAccessListTracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { | 
|  | 69 | +	// any self-destructed accounts must have been created in the same transaction | 
|  | 70 | +	// so there is no difference between the pre/post tx state of those accounts | 
|  | 71 | +	if a.selfdestructedAccount != nil { | 
|  | 72 | +		delete(a.callAccessLists[len(a.callAccessLists)-1].Accounts, *a.selfdestructedAccount) | 
|  | 73 | +	} | 
|  | 74 | +	if !reverted { | 
|  | 75 | +		parentAccessList := a.callAccessLists[len(a.callAccessLists)-2] | 
|  | 76 | +		scopeAccessList := a.callAccessLists[len(a.callAccessLists)-1] | 
|  | 77 | +		parentAccessList.Merge(scopeAccessList) | 
|  | 78 | +	} | 
|  | 79 | + | 
|  | 80 | +	a.callAccessLists = a.callAccessLists[:len(a.callAccessLists)-1] | 
|  | 81 | +} | 
|  | 82 | + | 
|  | 83 | +func (a *BlockAccessListTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason tracing.CodeChangeReason) { | 
|  | 84 | +	if reason == tracing.CodeChangeSelfDestruct { | 
|  | 85 | +		a.selfdestructedAccount = &addr | 
|  | 86 | +		return | 
|  | 87 | +	} | 
|  | 88 | +	a.callAccessLists[len(a.callAccessLists)-1].CodeChange(addr, uint16(a.txIdx), code) | 
|  | 89 | +} | 
|  | 90 | + | 
|  | 91 | +func (a *BlockAccessListTracer) OnBalanceChange(addr common.Address, prevBalance, newBalance *big.Int, _ tracing.BalanceChangeReason) { | 
|  | 92 | +	a.callAccessLists[len(a.callAccessLists)-1].BalanceChange(a.txIdx, addr, new(uint256.Int).SetBytes(newBalance.Bytes())) | 
|  | 93 | +} | 
|  | 94 | + | 
|  | 95 | +func (a *BlockAccessListTracer) OnNonceChange(addr common.Address, prev uint64, new uint64) { | 
|  | 96 | +	a.callAccessLists[len(a.callAccessLists)-1].NonceChange(addr, a.txIdx, new) | 
|  | 97 | +} | 
|  | 98 | + | 
|  | 99 | +func (a *BlockAccessListTracer) OnColdStorageRead(addr common.Address, key common.Hash) { | 
|  | 100 | +	a.callAccessLists[len(a.callAccessLists)-1].StorageRead(addr, key) | 
|  | 101 | +} | 
|  | 102 | + | 
|  | 103 | +func (a *BlockAccessListTracer) OnColdAccountRead(addr common.Address) { | 
|  | 104 | +	a.callAccessLists[len(a.callAccessLists)-1].AccountRead(addr) | 
|  | 105 | +} | 
|  | 106 | + | 
|  | 107 | +func (a *BlockAccessListTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) { | 
|  | 108 | +	a.callAccessLists[len(a.callAccessLists)-1].StorageWrite(a.txIdx, addr, slot, new) | 
|  | 109 | +} | 
0 commit comments