diff --git a/eth/tracers/api.go b/eth/tracers/api.go index b119ffd643..fdc1a91e6e 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -704,6 +704,81 @@ func (api *API) executeTx(ctx context.Context, index int, block *types.Block, tx return nil } +type txAccessList struct { + Accesslist *types.AccessList `json:"accessList"` + Error string `json:"error,omitempty"` + Hash common.Hash `json:"hash"` +} + +func (api *API) GetBlockAccessLists(ctx context.Context, blockNum uint64) ([]txAccessList, error) { + + if blockNum == 0 { + return []txAccessList{}, errors.New("genesis block is not applicable") + } + block, err := api.blockByNumber(ctx, rpc.BlockNumber(blockNum)) + if err != nil { + return []txAccessList{}, err + } + + parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(blockNum-1), block.ParentHash()) + if err != nil { + return []txAccessList{}, fmt.Errorf("failed to get parent block: parent: %s, err %v", block.ParentHash().Hex(), err) + } + //generate the state at the parent block + statedb, release, err := api.backend.StateAtBlock(ctx, parent, defaultTraceReexec, nil, true, false) + if err != nil { + return []txAccessList{}, fmt.Errorf("failed to generate state at the parent block: err %v", err) + } + defer release() + + return api.blockAccessLists(ctx, block, statedb) +} + +func (api *API) blockAccessLists(ctx context.Context, block *types.Block, statedb *state.StateDB) ([]txAccessList, error) { + + var ( + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) + postMerge = block.Difficulty().Sign() == 0 + chainConfig = api.backend.ChainConfig() + vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + precompiles = vm.ActivePrecompiles(api.backend.ChainConfig().Rules(block.Number(), postMerge, block.Time())) + evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) + acls []txAccessList + ) + if beaconRoot := block.BeaconRoot(); beaconRoot != nil { + core.ProcessBeaconBlockRoot(*beaconRoot, evm) + } + if chainConfig.IsPrague(block.Number(), block.Time()) { + core.ProcessParentBlockHash(block.ParentHash(), evm) + } + + var to common.Address + for ind, tx := range block.Transactions() { + from, _ := types.Sender(signer, tx) + // create an access list tracer + if tx.To() != nil { + to = *tx.To() + } + tracer := logger.NewAccessListTracer(nil, from, to, precompiles) + evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true}) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + statedb.SetTxContext(tx.Hash(), ind) + res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) + if err != nil { + return acls, fmt.Errorf("failed to apply tx: err %v", err) + } + acl := tracer.AccessList() + // tracer.PrintAccessList() + txAcl := txAccessList{Accesslist: &acl, Hash: tx.Hash()} + if res.Err != nil { + txAcl.Error = res.Err.Error() + } + acls = append(acls, txAcl) + } + + return acls, nil +} + // StandardTraceBadBlockToFile dumps the structured logs created during the // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index e8231461b0..a22a7c4e3e 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" ) // accessList is an accumulator for the set of accounts and storage slots an EVM @@ -93,6 +94,17 @@ func (al accessList) accessList() types.AccessList { return acl } +func (al accessList) Print() { + log.Info("printing access list") + for k, v := range al { + log.Info("entry", "address", k) + for s := range v { + log.Info("slot", "hash", s.Hex()) + } + } + +} + // AccessListTracer is a tracer that accumulates touched accounts and storage // slots into an internal set. type AccessListTracer struct { @@ -159,6 +171,15 @@ func (a *AccessListTracer) AccessList() types.AccessList { return a.list.accessList() } +func (a *AccessListTracer) PrintAccessList() { + log.Info("excluded access entries, this may contain precompiled contracts") + for k, _ := range a.excl { + log.Info("entry", "address", k.Hex()) + } + log.Info("Printing other accesses") + a.list.Print() +} + // Equal returns if the content of two access list traces are equal. func (a *AccessListTracer) Equal(other *AccessListTracer) bool { return a.list.equal(other.list) diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 170e14dad2..a56cb8b01d 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -392,6 +392,12 @@ web3._extend({ params: 2, inputFormatter: [null, null], }), + new web3._extend.Method({ + name: 'getBlockAccessLists', + call: 'debug_getBlockAccessLists', + params: 1, + inputFormatter: [null], + }), new web3._extend.Method({ name: 'traceBlockByHash', call: 'debug_traceBlockByHash',