Commit 5c54f884 authored by Axel Kingsley's avatar Axel Kingsley Committed by GitHub

interop: Fix AtLeastAsSafe ; Geth Mempool Filter E2E Test (#12823)

* interop: Geth Mempool Filter E2E Test

* use latest op-geth rc

* fix AtLeastAsSafe

* fix test
parent dad10877
......@@ -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
......
......@@ -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=
......
......@@ -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)
})
}
......@@ -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)
}
......
......@@ -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 (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment