diff --git a/arbitrum/retryables/scheduled_txes.go b/arbitrum/retryables/scheduled_txes.go index 029c4425a0..3fdabc68e7 100644 --- a/arbitrum/retryables/scheduled_txes.go +++ b/arbitrum/retryables/scheduled_txes.go @@ -12,10 +12,16 @@ import ( ) // RunScheduledTxes executes scheduled transactions (retryable redeems) including -// cascading redeems. When txFilterer is non-nil, touches addresses for filtering. +// cascading redeems. It runs when either gas estimation is active (to account for +// scheduled gas usage) or when txFilterer is non-nil (to execute scheduled txes +// and collect their touched addresses for address-based filtering). func RunScheduledTxes(ctx context.Context, b core.NodeInterfaceBackendAPI, statedb *state.StateDB, header *types.Header, blockCtx vm.BlockContext, runCtx *core.MessageRunContext, result *core.ExecutionResult, txFilterer core.TxFilterer) (*core.ExecutionResult, error) { + // If we're not doing gas estimation and there's no tx filter, we don't need to run scheduled txes at all. + if !runCtx.IsGasEstimation() && txFilterer == nil { + return result, nil + } scheduled := result.ScheduledTxes - for runCtx.IsGasEstimation() && len(scheduled) > 0 { + for len(scheduled) > 0 { // This will panic if the scheduled tx is signed, but we only schedule unsigned ones msg, err := core.TransactionToMessage(scheduled[0], types.NewArbitrumSigner(nil), header.BaseFee, runCtx) if err != nil { @@ -26,11 +32,14 @@ func RunScheduledTxes(ctx context.Context, b core.NodeInterfaceBackendAPI, state txFilterer.TouchAddresses(statedb, scheduled[0], msg.From) } - if result.UsedGas >= msg.GasLimit { - result.UsedGas -= msg.GasLimit - } else { - log.Warn("Scheduling tx used less gas than scheduled tx has available", "usedGas", result.UsedGas, "scheduledGas", msg.GasLimit) - result.UsedGas = 0 + // For gas estimation, we want to account scheduled txes' gas usage + if runCtx.IsGasEstimation() { + if result.UsedGas >= msg.GasLimit { + result.UsedGas -= msg.GasLimit + } else { + log.Warn("Scheduling tx used less gas than scheduled tx has available", "usedGas", result.UsedGas, "scheduledGas", msg.GasLimit) + result.UsedGas = 0 + } } evm := b.GetEVM(ctx, statedb, header, &vm.Config{NoBaseFee: true}, &blockCtx) @@ -47,9 +56,14 @@ func RunScheduledTxes(ctx context.Context, b core.NodeInterfaceBackendAPI, state return nil, vmerr } if scheduledTxResult.Failed() { - return scheduledTxResult, nil + if runCtx.IsGasEstimation() { + return scheduledTxResult, nil + } + return result, nil + } + if runCtx.IsGasEstimation() { + result.UsedGas += scheduledTxResult.UsedGas } - result.UsedGas += scheduledTxResult.UsedGas scheduled = append(scheduled[1:], scheduledTxResult.ScheduledTxes...) } return result, nil diff --git a/core/arbitrum_hooks.go b/core/arbitrum_hooks.go index 6512af8bc5..1674028438 100644 --- a/core/arbitrum_hooks.go +++ b/core/arbitrum_hooks.go @@ -55,10 +55,11 @@ var RenderRPCError func(data []byte) error type TxFilterer interface { // Setup activates address filtering on statedb. Setup(statedb *state.StateDB) - // TouchAddresses marks sender, recipient, aliased, and retryable addresses for filtering. + // TouchAddresses marks sender, recipient, aliased, and retryable addresses + // of a transaction for filtering. TouchAddresses(statedb *state.StateDB, tx *types.Transaction, sender common.Address) - // CheckFiltered applies event filtering and returns state.ErrArbTxFilter if filtered. - CheckFiltered(statedb *state.StateDB) error + // ApplyEventsAndCheckFiltered applies event filtering and returns state.ErrArbTxFilter if any touched address is filtered. + ApplyEventsAndCheckFiltered(statedb *state.StateDB) error } type NodeInterfaceBackendAPI interface { diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go index af988f2f25..5e2213c38e 100644 --- a/eth/gasestimator/gasestimator.go +++ b/eth/gasestimator/gasestimator.go @@ -285,7 +285,6 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio txFilterer := opts.Backend.TxFilter() if txFilterer != nil { txFilterer.Setup(dirtyState) - dirtyState.TouchAddress(&filter.FilteredAddressRecord{Address: call.From, FilterReason: filter.FilterReason{Reason: filter.ReasonFrom, EventRuleMatch: nil}}) if call.To != nil { dirtyState.TouchAddress(&filter.FilteredAddressRecord{Address: *call.To, FilterReason: filter.FilterReason{Reason: filter.ReasonTo, EventRuleMatch: nil}}) @@ -314,7 +313,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio // Arbitrum: check address filtering result if txFilterer != nil { - if err := txFilterer.CheckFiltered(dirtyState); err != nil { + if err := txFilterer.ApplyEventsAndCheckFiltered(dirtyState); err != nil { return nil, err } } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0b2e3ad4dc..bcc95a7265 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -28,6 +28,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/arbitrum/filter" "github.com/ethereum/go-ethereum/arbitrum/retryables" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -779,6 +780,16 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if err != nil || res != nil { return res, err } + + // Arbitrum: set up address filtering (after InterceptRPCMessage, matching gasestimator order) + txFilterer := b.TxFilter() + if txFilterer != nil { + txFilterer.Setup(state) + state.TouchAddress(&filter.FilteredAddressRecord{Address: msg.From, FilterReason: filter.FilterReason{Reason: filter.ReasonFrom, EventRuleMatch: nil}}) + if msg.To != nil { + state.TouchAddress(&filter.FilteredAddressRecord{Address: *msg.To, FilterReason: filter.FilterReason{Reason: filter.ReasonTo, EventRuleMatch: nil}}) + } + } // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { @@ -798,6 +809,15 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if err := state.Error(); err != nil { return nil, err } + // Arbitrum: run scheduled txes for address filtering and check result + if txFilterer != nil && err == nil { + if _, ferr := retryables.RunScheduledTxes(ctx, b, state, header, *blockContext, msg.TxRunContext, res, txFilterer); ferr != nil { + return nil, ferr + } + if ferr := txFilterer.ApplyEventsAndCheckFiltered(state); ferr != nil { + return nil, ferr + } + } return res, err }