Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions accounts/abi/bind/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
GasFeeCap: gasFeeCap,
Value: value,
Data: input,
// OP-Stack fix: important for CrossL2Inbox gas estimation
AccessList: opts.AccessList,
}
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
}
Expand Down
11 changes: 0 additions & 11 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,21 +225,10 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) {
return ApplyTransactionExtended(evm, gp, statedb, header, tx, usedGas, nil)
}

type ApplyTransactionOpts struct {
PostValidation func(evm *vm.EVM, result *ExecutionResult) error
}

func ApplyTransactionExtended(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, extraOpts *ApplyTransactionOpts) (*types.Receipt, error) {
msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee)
if err != nil {
return nil, err
}
if extraOpts != nil {
msg.PostValidation = extraOpts.PostValidation
}
// Create a new context to be used in the EVM environment
return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm)
}
Expand Down
19 changes: 3 additions & 16 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,6 @@ type Message struct {
IsDepositTx bool // IsDepositTx indicates the message is force-included and can persist a mint.
Mint *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting.
RollupCostData types.RollupCostData // RollupCostData caches data to compute the fee we charge for data availability

// PostValidation is an optional check of the resulting post-state, if and when the message is
// applied fully to the EVM. This function may return an error to deny inclusion of the message.
PostValidation func(evm *vm.EVM, result *ExecutionResult) error
}

// TransactionToMessage converts a transaction into a Message.
Expand Down Expand Up @@ -490,13 +486,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
err = nil
}

if err == nil && st.msg.PostValidation != nil {
if err := st.msg.PostValidation(st.evm, result); err != nil {
return nil, err
}
}

return result, err
}

Expand Down Expand Up @@ -637,11 +626,6 @@ func (st *stateTransition) innerExecute() (*ExecutionResult, error) {
}
}
}
if rules.IsOptimismIsthmus {
// Calling st.refundOperatorCost() after st.gasRemaining is updated above,
// so that state refunds are taken into account when calculating operator fees.
st.refundIsthmusOperatorCost()
}
st.returnGas()

// OP-Stack: Note for deposit tx there is no ETH refunded for unused gas, but that's taken care of by the fact that gasPrice
Expand Down Expand Up @@ -697,6 +681,9 @@ func (st *stateTransition) innerExecute() (*ExecutionResult, error) {
st.state.AddBalance(params.OptimismL1FeeRecipient, amtU256, tracing.BalanceIncreaseRewardTransactionFee)
}
if rules.IsOptimismIsthmus {
// Operator Fee refunds are only applied if Isthmus is active and the transaction is *not* a deposit.
st.refundIsthmusOperatorCost()

operatorFeeCost := st.evm.Context.OperatorCostFunc(st.gasUsed(), st.evm.Context.Time)
st.state.AddBalance(params.OptimismOperatorFeeRecipient, operatorFeeCost, tracing.BalanceIncreaseRewardTransactionFee)
}
Expand Down
64 changes: 33 additions & 31 deletions core/txpool/ingress_filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import (
"context"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/interoptypes"
"github.com/ethereum/go-ethereum/log"
)

// IngressFilter is an interface that allows filtering of transactions before they are added to the transaction pool.
Expand All @@ -16,44 +16,46 @@ type IngressFilter interface {
FilterTx(ctx context.Context, tx *types.Transaction) bool
}

type interopFilter struct {
logsFn func(tx *types.Transaction) (logs []*types.Log, logTimestamp uint64, err error)
checkFn func(ctx context.Context, ems []interoptypes.Message, safety interoptypes.SafetyLevel, emsTimestamp uint64) error
type interopFilterAPI interface {
CurrentInteropBlockTime() (uint64, error)
TxToInteropAccessList(tx *types.Transaction) []common.Hash
CheckAccessList(ctx context.Context, inboxEntries []common.Hash, minSafety interoptypes.SafetyLevel, execDesc interoptypes.ExecutingDescriptor) error
}

func NewInteropFilter(
logsFn func(tx *types.Transaction) ([]*types.Log, uint64, error),
checkFn func(ctx context.Context, ems []interoptypes.Message, safety interoptypes.SafetyLevel, emsTimestamp uint64) error) IngressFilter {
return &interopFilter{
logsFn: logsFn,
checkFn: checkFn,
type interopAccessFilter struct {
api interopFilterAPI
timeout uint64
}

// NewInteropFilter creates a new IngressFilter that filters transactions based on the interop access list.
// the timeout is set to 1 day, the specified preverifier window
func NewInteropFilter(api interopFilterAPI) IngressFilter {
return &interopAccessFilter{
api: api,
timeout: 86400,
}
}

// FilterTx implements IngressFilter.FilterTx
// it gets logs checks for message safety based on the function provided
func (f *interopFilter) FilterTx(ctx context.Context, tx *types.Transaction) bool {
logs, logTimestamp, err := f.logsFn(tx)
if err != nil {
log.Debug("Failed to retrieve logs of tx", "txHash", tx.Hash(), "err", err)
return false // default to deny if logs cannot be retrieved
// it uses provided functions to get the access list from the transaction
// and check it against the supervisor
func (f *interopAccessFilter) FilterTx(ctx context.Context, tx *types.Transaction) bool {
hashes := f.api.TxToInteropAccessList(tx)
// if there are no interop access list entries, allow the transaction (there is no interop check to perform)
if len(hashes) == 0 {
return true
}
if len(logs) == 0 {
return true // default to allow if there are no logs
}
ems, err := interoptypes.ExecutingMessagesFromLogs(logs)
t, err := f.api.CurrentInteropBlockTime()
// if there are interop access list entries, but the interop API is not available, reject the transaction
if err != nil {
log.Debug("Failed to parse executing messages of tx", "txHash", tx.Hash(), "err", err)
return false // default to deny if logs cannot be parsed
return false
}
if len(ems) == 0 {
return true // default to allow if there are no executing messages
// if the transaction is older than the preverifier window, reject it eagerly
expireTime := time.Unix(int64(t), 0).Add(time.Duration(-f.timeout) * time.Second)
if tx.Time().Compare(expireTime) < 0 {
return false
}

ctx, cancel := context.WithTimeout(ctx, time.Second*2)
defer cancel()
// check with the supervisor if the transaction should be allowed given the executing messages
// the message can be unsafe (discovered only via P2P unsafe blocks), but it must be cross-valid
// so CrossUnsafe is used here
return f.checkFn(ctx, ems, interoptypes.CrossUnsafe, logTimestamp) == nil
exDesc := interoptypes.ExecutingDescriptor{Timestamp: t, Timeout: f.timeout}
// perform the interop check
return f.api.CheckAccessList(ctx, hashes, interoptypes.CrossUnsafe, exDesc) == nil
}
Loading