diff --git a/go.mod b/go.mod index 66c99169912c..c1b75b810176 100644 --- a/go.mod +++ b/go.mod @@ -250,7 +250,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 +replace github.com/ethereum/go-ethereum v1.14.11 => github.com/ethereum-optimism/op-geth v1.101411.1-rc.6 //replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/go.sum b/go.sum index 3eaeda632c7b..df4fc67449c3 100644 --- a/go.sum +++ b/go.sum @@ -187,8 +187,8 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.5 h1:LDSP85xTczjDYMBK0mOF5mzpZifLGz1TTW/6NgQsytc= -github.com/ethereum-optimism/op-geth v1.101411.1-rc.5/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.6 h1:VvUBIVFbnU9486CWHa9Js5XYY3o6OsdQcI8gE3XjCDE= +github.com/ethereum-optimism/op-geth v1.101411.1-rc.6/go.mod h1:7S4pp8KHBmEmKkRjL1BPOc6jY9hW+64YeMUjR3RVLw4= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac h1:hCIrLuOPV3FJfMDvXeOhCC3uQNvFoMIIlkT2mN2cfeg= github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240910145426-b3905c89e8ac/go.mod h1:XaVXL9jg8BcyOeugECgIUGa9Y3DjYJj71RHmb5qon6M= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= diff --git a/op-e2e/interop/interop_test.go b/op-e2e/interop/interop_test.go index 65efa9b84209..302413dd98ed 100644 --- a/op-e2e/interop/interop_test.go +++ b/op-e2e/interop/interop_test.go @@ -20,12 +20,13 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/interopgen" "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" + gethCore "github.com/ethereum/go-ethereum/core" gethTypes "github.com/ethereum/go-ethereum/core/types" ) // setupAndRun is a helper function that sets up a SuperSystem // which contains two L2 Chains, and two users on each chain. -func setupAndRun(t *testing.T, fn func(*testing.T, SuperSystem)) { +func setupAndRun(t *testing.T, config SuperSystemConfig, fn func(*testing.T, SuperSystem)) { recipe := interopgen.InteropDevRecipe{ L1ChainID: 900100, L2ChainIDs: []uint64{900200, 900201}, @@ -38,7 +39,7 @@ func setupAndRun(t *testing.T, fn func(*testing.T, SuperSystem)) { // create a super system from the recipe // and get the L2 IDs for use in the test - s2 := NewSuperSystem(t, &recipe, worldResources) + s2 := NewSuperSystem(t, &recipe, worldResources, config) // create two users on all L2 chains s2.AddUser("Alice") @@ -98,13 +99,16 @@ func TestInterop_IsolatedChains(t *testing.T) { expectedBalance, _ = big.NewInt(0).SetString("10000000000000000000000000", 10) require.Equal(t, expectedBalance, bobBalance) } - setupAndRun(t, test) + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) } -// TestInteropTrivial_EmitLogs tests a simple interop scenario +// TestInterop_EmitLogs tests a simple interop scenario // Chains A and B exist, but no messages are sent between them. // A contract is deployed on each chain, and logs are emitted repeatedly. -func TestInteropTrivial_EmitLogs(t *testing.T) { +func TestInterop_EmitLogs(t *testing.T) { test := func(t *testing.T, s2 SuperSystem) { ids := s2.L2IDs() chainA := ids[0] @@ -195,7 +199,10 @@ func TestInteropTrivial_EmitLogs(t *testing.T) { requireMessage(chainB, log, types.CrossSafe, nil) } } - setupAndRun(t, test) + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) } func TestInteropBlockBuilding(t *testing.T) { @@ -271,11 +278,17 @@ func TestInteropBlockBuilding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() // Send an executing message, but with different payload. - // We expect the miner to be unable to include this tx, and confirmation to thus time out. - _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload) - require.NotNil(t, err) - require.ErrorIs(t, err, ctx.Err()) - require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded) + if s2.(*interopE2ESystem).config.mempoolFiltering { + // We expect the traqnsaction to be filtered out by the mempool if mempool filtering is enabled. + // ExecuteMessage the ErrTxFilteredOut error is checked when sending the tx. + _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload, gethCore.ErrTxFilteredOut) + require.ErrorContains(t, err, gethCore.ErrTxFilteredOut.Error()) + } else { + // We expect the miner to be unable to include this tx, and confirmation to thus time out, if mempool filtering is disabled. + _, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, invalidPayload, nil) + require.ErrorIs(t, err, ctx.Err()) + require.ErrorIs(t, ctx.Err(), context.DeadlineExceeded) + } } t.Log("Testing valid message now") @@ -284,11 +297,25 @@ func TestInteropBlockBuilding(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() // Send an executing message with the correct identifier / payload - rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload) + rec, err := s2.ExecuteMessage(ctx, chainB, "Alice", identifier, bobAddr, msgPayload, nil) require.NoError(t, err, "expecting tx to be confirmed") t.Logf("confirmed executing msg in block %s", rec.BlockNumber) } t.Log("Done") } - setupAndRun(t, test) + + t.Run("without mempool filtering", func(t *testing.T) { + config := SuperSystemConfig{ + mempoolFiltering: false, + } + setupAndRun(t, config, test) + }) + + t.Run("with mempool filtering", func(t *testing.T) { + config := SuperSystemConfig{ + mempoolFiltering: true, + } + // run again with mempool filtering to observe the behavior of the mempool filter + setupAndRun(t, config, test) + }) } diff --git a/op-e2e/interop/supersystem.go b/op-e2e/interop/supersystem.go index f18c7475073e..de7e25bb9c7b 100644 --- a/op-e2e/interop/supersystem.go +++ b/op-e2e/interop/supersystem.go @@ -114,14 +114,18 @@ type SuperSystem interface { msgIdentifier supervisortypes.Identifier, target common.Address, message []byte, + expectedError error, ) (*types.Receipt, error) // Access a contract on a network by name Contract(network string, contractName string) interface{} } +type SuperSystemConfig struct { + mempoolFiltering bool +} // NewSuperSystem creates a new SuperSystem from a recipe. It creates an interopE2ESystem. -func NewSuperSystem(t *testing.T, recipe *interopgen.InteropDevRecipe, w worldResourcePaths) SuperSystem { - s2 := &interopE2ESystem{recipe: recipe} +func NewSuperSystem(t *testing.T, recipe *interopgen.InteropDevRecipe, w worldResourcePaths, config SuperSystemConfig) SuperSystem { + s2 := &interopE2ESystem{recipe: recipe, config: &config} s2.prepare(t, w) return s2 } @@ -144,6 +148,7 @@ type interopE2ESystem struct { l2GethClients map[string]*ethclient.Client supervisor *supervisor.SupervisorService superClient *sources.SupervisorClient + config *SuperSystemConfig } // l2Set is a set of resources for an L2 chain @@ -263,6 +268,7 @@ func (s *interopE2ESystem) newGethForL2(id string, l2Out *interopgen.L2Output) * l2Geth, err := geth.InitL2(name, l2Out.Genesis, jwtPath, func(ethCfg *ethconfig.Config, nodeCfg *gn.Config) error { ethCfg.InteropMessageRPC = s.supervisor.RPC() + ethCfg.InteropMempoolFiltering = s.config.mempoolFiltering return nil }) require.NoError(s.t, err) @@ -721,6 +727,10 @@ func (s *interopE2ESystem) SendL2Tx( newApply) } +// ExecuteMessage calls the CrossL2Inbox executeMessage function +// it uses the L2's chain ID, username key, and geth client. +// expectedError represents the error returned by `ExecuteMessage` if it is expected. +// the returned err is related to `WaitMined` func (s *interopE2ESystem) ExecuteMessage( ctx context.Context, id string, @@ -728,6 +738,7 @@ func (s *interopE2ESystem) ExecuteMessage( msgIdentifier supervisortypes.Identifier, target common.Address, message []byte, + expectedError error, ) (*types.Receipt, error) { secret := s.UserKey(id, sender) auth, err := bind.NewKeyedTransactorWithChainID(&secret, s.l2s[id].chainID) @@ -746,7 +757,12 @@ func (s *interopE2ESystem) ExecuteMessage( ChainId: msgIdentifier.ChainID.ToBig(), } tx, err := contract.InboxTransactor.ExecuteMessage(auth, identifier, target, message) - require.NoError(s.t, err) + if expectedError != nil { + require.ErrorContains(s.t, err, expectedError.Error()) + return nil, err + } else { + require.NoError(s.t, err) + } s.logger.Info("Executing message", "tx", tx.Hash(), "to", tx.To(), "target", target, "data", hexutil.Bytes(tx.Data())) return bind.WaitMined(ctx, s.L2GethClient(id), tx) } diff --git a/op-supervisor/supervisor/types/types.go b/op-supervisor/supervisor/types/types.go index bac2c377bfd7..b0b601a0c9bc 100644 --- a/op-supervisor/supervisor/types/types.go +++ b/op-supervisor/supervisor/types/types.go @@ -131,19 +131,27 @@ func (lvl *SafetyLevel) UnmarshalText(text []byte) error { } // AtLeastAsSafe returns true if the receiver is at least as safe as the other SafetyLevel. +// Safety levels are assumed to graduate from LocalUnsafe to LocalSafe to CrossUnsafe to CrossSafe, with Finalized as the strongest. func (lvl *SafetyLevel) AtLeastAsSafe(min SafetyLevel) bool { - switch min { - case Invalid: - return true - case CrossUnsafe: - return *lvl != Invalid - case CrossSafe: - return *lvl == CrossSafe || *lvl == Finalized - case Finalized: - return *lvl == Finalized - default: + relativeSafety := map[SafetyLevel]int{ + Invalid: 0, + LocalUnsafe: 1, + LocalSafe: 2, + CrossUnsafe: 3, + CrossSafe: 4, + Finalized: 5, + } + // if either level is not recognized, return false + _, ok := relativeSafety[*lvl] + if !ok { + return false + } + _, ok = relativeSafety[min] + if !ok { return false } + // compare the relative safety levels to determine if the receiver is at least as safe as the other + return relativeSafety[*lvl] >= relativeSafety[min] } const (