diff --git a/op-acceptance-tests/tests/fjord/check_scripts_test.go b/op-acceptance-tests/tests/fjord/check_scripts_test.go index 772e262ae22..649f4a5e9de 100644 --- a/op-acceptance-tests/tests/fjord/check_scripts_test.go +++ b/op-acceptance-tests/tests/fjord/check_scripts_test.go @@ -165,7 +165,7 @@ func checkFastLZTransactions(t devtest.T, ctx context.Context, sys *presets.Mini fastLzSize := uint64(types.FlzCompressLen(txUnsigned) + 68) gethGPOFee, err := dsl.CalculateFjordL1Cost(ctx, l2Client, types.RollupCostData{FastLzSize: fastLzSize}, receipt.BlockHash) require.NoError(err) - require.Equal(gethGPOFee.Uint64(), gpoFee.Uint64()) + require.Equalf(gethGPOFee.Uint64(), gpoFee.Uint64(), "GPO L1 fee mismatch (expected=%d actual=%d)", gethGPOFee.Uint64(), gpoFee.Uint64()) expectedFee, err := dsl.CalculateFjordL1Cost(ctx, l2Client, signedTx.RollupCostData(), receipt.BlockHash) require.NoError(err) @@ -178,7 +178,7 @@ func checkFastLZTransactions(t devtest.T, ctx context.Context, sys *presets.Mini flzUpperBound := uint64(txLenGPO + txLenGPO/255 + 16) upperBoundCost, err := dsl.CalculateFjordL1Cost(ctx, l2Client, types.RollupCostData{FastLzSize: flzUpperBound}, receipt.BlockHash) require.NoError(err) - require.Equal(upperBoundCost.Uint64(), upperBound.Uint64()) + require.Equalf(upperBoundCost.Uint64(), upperBound.Uint64(), "GPO L1 upper bound mismatch (expected=%d actual=%d)", upperBoundCost.Uint64(), upperBound.Uint64()) _, err = contractio.Read(gasPriceOracle.BaseFeeScalar(), ctx) require.NoError(err) diff --git a/op-acceptance-tests/tests/interop/interop_smoke_test.go b/op-acceptance-tests/tests/interop/interop_smoke_test.go deleted file mode 100644 index 2fa1be41b36..00000000000 --- a/op-acceptance-tests/tests/interop/interop_smoke_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package interop - -import ( - "context" - "math/big" - "testing" - - "github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants" - "github.com/ethereum-optimism/optimism/devnet-sdk/system" - "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" - "github.com/ethereum-optimism/optimism/devnet-sdk/testing/testlib/validators" - sdktypes "github.com/ethereum-optimism/optimism/devnet-sdk/types" - "github.com/ethereum-optimism/optimism/op-service/testlog" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - "github.com/stretchr/testify/require" -) - -func smokeTestScenario(chainIdx uint64, walletGetter validators.WalletGetter) systest.SystemTestFunc { - return func(t systest.T, sys system.System) { - ctx := t.Context() - - logger := testlog.Logger(t, log.LevelInfo) - logger = logger.With("test", "TestMinimal", "devnet", sys.Identifier()) - - chain := sys.L2s()[chainIdx] - logger = logger.With("chain", chain.ID()) - logger.Info("starting test") - - funds := sdktypes.NewBalance(big.NewInt(1 * constants.ETH)) - user := walletGetter(ctx) - - wethAddr := constants.WETH - weth, err := chain.Nodes()[0].ContractsRegistry().WETH(wethAddr) - require.NoError(t, err) - initialBalance, err := weth.BalanceOf(user.Address()).Call(ctx) - require.NoError(t, err) - logger = logger.With("user", user.Address()) - logger.Info("initial balance retrieved", "balance", initialBalance) - - logger.Info("sending ETH to contract", "amount", funds) - require.NoError(t, user.SendETH(wethAddr, funds).Send(ctx).Wait()) - - balance, err := weth.BalanceOf(user.Address()).Call(ctx) - require.NoError(t, err) - logger.Info("final balance retrieved", "balance", balance) - - require.Equal(t, initialBalance.Add(funds), balance) - } -} - -func TestInteropSystemNoop(t *testing.T) { - systest.InteropSystemTest(t, func(t systest.T, sys system.InteropSystem) { - testlog.Logger(t, log.LevelInfo).Info("noop") - }) -} - -func TestSmokeTestFailure(t *testing.T) { - // Create mock failing system - mockAddr := common.HexToAddress("0x1234567890123456789012345678901234567890") - mockWallet := &mockFailingWallet{ - addr: mockAddr, - bal: sdktypes.NewBalance(big.NewInt(0.1 * constants.ETH)), - } - mockL1Chain := newMockFailingL1Chain( - sdktypes.ChainID(big.NewInt(1234)), - system.WalletMap{ - "user1": mockWallet, - }, - []system.Node{&mockFailingNode{ - reg: &mockContractsRegistry{}, - }}, - ) - mockL2Chain := newMockFailingL2Chain( - sdktypes.ChainID(big.NewInt(1234)), - system.WalletMap{"user1": mockWallet}, - []system.Node{&mockFailingNode{ - reg: &mockContractsRegistry{}, - }}, - ) - mockSys := &mockFailingSystem{l1Chain: mockL1Chain, l2Chain: mockL2Chain} - - // Run the smoke test logic and capture failures - getter := func(ctx context.Context) system.Wallet { - return mockWallet - } - rt := NewRecordingT(context.TODO()) - rt.TestScenario( - smokeTestScenario(0, getter), - mockSys, - ) - - // Verify that the test failed due to SendETH error - require.True(t, rt.Failed(), "test should have failed") - require.Contains(t, rt.Logs(), "transaction failure", "unexpected failure message") -} diff --git a/op-acceptance-tests/tests/interop/mocks_test.go b/op-acceptance-tests/tests/interop/mocks_test.go deleted file mode 100644 index fe41f0d545a..00000000000 --- a/op-acceptance-tests/tests/interop/mocks_test.go +++ /dev/null @@ -1,396 +0,0 @@ -package interop - -import ( - "bytes" - "context" - "fmt" - "math/big" - "os" - "runtime" - "time" - - "github.com/ethereum-optimism/optimism/devnet-sdk/contracts/bindings" - "github.com/ethereum-optimism/optimism/devnet-sdk/contracts/registry/empty" - "github.com/ethereum-optimism/optimism/devnet-sdk/interfaces" - "github.com/ethereum-optimism/optimism/devnet-sdk/system" - "github.com/ethereum-optimism/optimism/devnet-sdk/testing/systest" - "github.com/ethereum-optimism/optimism/devnet-sdk/types" - "github.com/ethereum-optimism/optimism/op-service/eth" - "github.com/ethereum-optimism/optimism/op-service/sources" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/params" -) - -var ( - // Ensure mockFailingTx implements WriteInvocation - _ types.WriteInvocation[any] = (*mockFailingTx)(nil) - - // Ensure mockFailingTx implements Wallet - _ system.Wallet = (*mockFailingWallet)(nil) - - // Ensure mockFailingChain implements Chain - _ system.Chain = (*mockFailingChain)(nil) - _ system.L2Chain = (*mockFailingL2Chain)(nil) -) - -// mockFailingTx implements types.WriteInvocation[any] that always fails -type mockFailingTx struct{} - -func (m *mockFailingTx) Call(ctx context.Context) (any, error) { - return nil, fmt.Errorf("simulated transaction failure") -} - -func (m *mockFailingTx) Send(ctx context.Context) types.InvocationResult { - return m -} - -func (m *mockFailingTx) Error() error { - return fmt.Errorf("transaction failure") -} - -func (m *mockFailingTx) Wait() error { - return fmt.Errorf("transaction failure") -} - -func (m *mockFailingTx) Info() any { - return nil -} - -// mockFailingWallet implements types.Wallet that fails on SendETH -type mockFailingWallet struct { - addr types.Address - key types.Key - bal types.Balance -} - -func (m *mockFailingWallet) Client() *ethclient.Client { - return nil -} - -func (m *mockFailingWallet) Address() types.Address { - return m.addr -} - -func (m *mockFailingWallet) PrivateKey() types.Key { - return m.key -} - -func (m *mockFailingWallet) Balance() types.Balance { - return m.bal -} - -func (m *mockFailingWallet) SendETH(to types.Address, amount types.Balance) types.WriteInvocation[any] { - return &mockFailingTx{} -} - -func (m *mockFailingWallet) InitiateMessage(chainID types.ChainID, target common.Address, message []byte) types.WriteInvocation[any] { - return &mockFailingTx{} -} - -func (m *mockFailingWallet) ExecuteMessage(identifier bindings.Identifier, sentMessage []byte) types.WriteInvocation[any] { - return &mockFailingTx{} -} - -func (m *mockFailingWallet) Nonce() uint64 { - return 0 -} - -func (m *mockFailingWallet) Sign(tx system.Transaction) (system.Transaction, error) { - return tx, nil -} - -func (m *mockFailingWallet) Send(ctx context.Context, tx system.Transaction) error { - return nil -} - -func (m *mockFailingWallet) Transactor() *bind.TransactOpts { - return nil -} - -// mockContractsRegistry extends empty.EmptyRegistry to provide mock contract instances -type mockContractsRegistry struct { - empty.EmptyRegistry -} - -// mockWETH implements a minimal WETH interface for testing -type mockWETH struct { - addr types.Address -} - -func (m *mockWETH) BalanceOf(account types.Address) types.ReadInvocation[types.Balance] { - return &mockReadInvocation{balance: types.NewBalance(big.NewInt(0))} -} - -// mockReadInvocation implements a read invocation that returns a fixed balance -type mockReadInvocation struct { - balance types.Balance -} - -func (m *mockReadInvocation) Call(ctx context.Context) (types.Balance, error) { - return m.balance, nil -} - -func (r *mockContractsRegistry) WETH(address types.Address) (interfaces.WETH, error) { - return &mockWETH{addr: address}, nil -} - -// mockFailingChain implements system.Chain with a failing SendETH -type mockFailingChain struct { - id types.ChainID - wallets system.WalletMap - nodes []system.Node -} - -var _ system.Chain = (*mockFailingChain)(nil) - -func newMockFailingL1Chain(id types.ChainID, wallets system.WalletMap, nodes []system.Node) *mockFailingChain { - return &mockFailingChain{ - id: id, - wallets: wallets, - nodes: nodes, - } -} - -func (m *mockFailingChain) Nodes() []system.Node { return m.nodes } -func (m *mockFailingChain) ID() types.ChainID { return m.id } -func (m *mockFailingChain) Wallets() system.WalletMap { - return m.wallets -} -func (m *mockFailingChain) Config() (*params.ChainConfig, error) { - return nil, fmt.Errorf("not implemented") -} -func (m *mockFailingChain) Addresses() system.AddressMap { - return map[string]common.Address{} -} - -var _ system.Node = (*mockFailingNode)(nil) - -type mockFailingNode struct { - reg interfaces.ContractsRegistry -} - -func (m *mockFailingNode) Client() (*sources.EthClient, error) { - return nil, fmt.Errorf("not implemented") -} -func (m *mockFailingNode) GasPrice(ctx context.Context) (*big.Int, error) { - return big.NewInt(1), nil -} -func (m *mockFailingNode) GasLimit(ctx context.Context, tx system.TransactionData) (uint64, error) { - return 1000000, nil -} -func (m *mockFailingNode) PendingNonceAt(ctx context.Context, address common.Address) (uint64, error) { - return 0, nil -} -func (m *mockFailingNode) SupportsEIP(ctx context.Context, eip uint64) bool { - return true -} -func (m *mockFailingNode) RPCURL() string { return "mock://failing" } -func (m *mockFailingNode) ContractsRegistry() interfaces.ContractsRegistry { return m.reg } -func (m *mockFailingNode) GethClient() (*ethclient.Client, error) { - return nil, fmt.Errorf("not implemented") -} -func (m *mockFailingNode) BlockByNumber(ctx context.Context, number *big.Int) (eth.BlockInfo, error) { - return nil, fmt.Errorf("not implemented") -} -func (m *mockFailingNode) Name() string { - return "mock" -} - -// mockFailingChain implements system.Chain with a failing SendETH -type mockFailingL2Chain struct { - mockFailingChain -} - -func newMockFailingL2Chain(id types.ChainID, wallets system.WalletMap, nodes []system.Node) *mockFailingL2Chain { - return &mockFailingL2Chain{ - mockFailingChain: mockFailingChain{ - id: id, - wallets: wallets, - nodes: nodes, - }, - } -} - -func (m *mockFailingL2Chain) L1Addresses() system.AddressMap { - return map[string]common.Address{} -} -func (m *mockFailingL2Chain) L1Wallets() system.WalletMap { - return map[string]system.Wallet{} -} - -// mockFailingSystem implements system.System -type mockFailingSystem struct { - l1Chain system.Chain - l2Chain system.L2Chain -} - -func (m *mockFailingSystem) Identifier() string { - return "mock-failing-system" -} - -func (m *mockFailingSystem) L1() system.Chain { - return m.l1Chain -} - -func (m *mockFailingSystem) L2s() []system.L2Chain { - return []system.L2Chain{m.l2Chain} -} - -func (m *mockFailingSystem) Close() error { - return nil -} - -// recordingT implements systest.T and records failures -type RecordingT struct { - failed bool - skipped bool - logs *bytes.Buffer - cleanup []func() - ctx context.Context -} - -func NewRecordingT(ctx context.Context) *RecordingT { - return &RecordingT{ - logs: bytes.NewBuffer(nil), - ctx: ctx, - } -} - -var _ systest.T = (*RecordingT)(nil) - -func (r *RecordingT) Context() context.Context { - return r.ctx -} - -func (r *RecordingT) WithContext(ctx context.Context) systest.T { - return &RecordingT{ - failed: r.failed, - skipped: r.skipped, - logs: r.logs, - cleanup: r.cleanup, - ctx: ctx, - } -} - -func (r *RecordingT) Deadline() (deadline time.Time, ok bool) { - // TODO - return time.Time{}, false -} - -func (r *RecordingT) Parallel() { - // TODO -} - -func (r *RecordingT) Run(name string, f func(systest.T)) { - // TODO -} - -func (r *RecordingT) Cleanup(f func()) { - r.cleanup = append(r.cleanup, f) -} - -func (r *RecordingT) Error(args ...interface{}) { - r.Log(args...) - r.Fail() -} - -func (r *RecordingT) Errorf(format string, args ...interface{}) { - r.Logf(format, args...) - r.Fail() -} - -func (r *RecordingT) Fatal(args ...interface{}) { - r.Log(args...) - r.FailNow() -} - -func (r *RecordingT) Fatalf(format string, args ...interface{}) { - r.Logf(format, args...) - r.FailNow() -} - -func (r *RecordingT) FailNow() { - r.Fail() - runtime.Goexit() -} - -func (r *RecordingT) Fail() { - r.failed = true -} - -func (r *RecordingT) Failed() bool { - return r.failed -} - -func (r *RecordingT) Helper() { - // TODO -} - -func (r *RecordingT) Log(args ...interface{}) { - fmt.Fprintln(r.logs, args...) -} - -func (r *RecordingT) Logf(format string, args ...interface{}) { - fmt.Fprintf(r.logs, format, args...) - fmt.Fprintln(r.logs) -} - -func (r *RecordingT) Name() string { - return "RecordingT" // TODO -} - -func (r *RecordingT) Setenv(key, value string) { - // Store original value - origValue, exists := os.LookupEnv(key) - - // Set new value - os.Setenv(key, value) - - // Register cleanup to restore original value - r.Cleanup(func() { - if exists { - os.Setenv(key, origValue) - } else { - os.Unsetenv(key) - } - }) - -} - -func (r *RecordingT) Skip(args ...interface{}) { - r.Log(args...) - r.SkipNow() -} - -func (r *RecordingT) SkipNow() { - r.skipped = true -} - -func (r *RecordingT) Skipf(format string, args ...interface{}) { - r.Logf(format, args...) - r.skipped = true -} - -func (r *RecordingT) Skipped() bool { - return r.skipped -} - -func (r *RecordingT) TempDir() string { - return "" // TODO -} - -func (r *RecordingT) Logs() string { - return r.logs.String() -} - -func (r *RecordingT) TestScenario(scenario systest.SystemTestFunc, sys system.System, values ...interface{}) { - // run in a separate goroutine so we can handle runtime.Goexit() - done := make(chan struct{}) - go func() { - defer close(done) - scenario(r, sys) - }() - <-done -} diff --git a/op-acceptance-tests/tests/interop/smoke/interop_smoke_test.go b/op-acceptance-tests/tests/interop/smoke/interop_smoke_test.go new file mode 100644 index 00000000000..c056b52b780 --- /dev/null +++ b/op-acceptance-tests/tests/interop/smoke/interop_smoke_test.go @@ -0,0 +1,89 @@ +package smoke + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-devstack/devtest" + "github.com/ethereum-optimism/optimism/op-devstack/presets" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/predeploys" + txib "github.com/ethereum-optimism/optimism/op-service/txintent/bindings" + "github.com/ethereum-optimism/optimism/op-service/txintent/contractio" + "github.com/ethereum/go-ethereum/core/types" +) + +func TestInteropSystemNoop(gt *testing.T) { + t := devtest.SerialT(gt) + _ = presets.NewMinimal(t) + t.Log("noop") +} + +func TestSmokeTest(gt *testing.T) { + t := devtest.SerialT(gt) + sys := presets.NewMinimal(t) + require := t.Require() + ctx := t.Ctx() + + user := sys.FunderL2.NewFundedEOA(eth.OneTenthEther) + + l2Client := sys.L2EL.Escape().EthClient() + weth := txib.NewBindings[txib.WETH]( + txib.WithClient(l2Client), + txib.WithTo(predeploys.WETHAddr), + txib.WithTest(t), + ) + + initialBalance, err := contractio.Read(weth.BalanceOf(user.Address()), ctx) + require.NoError(err) + t.Logf("Initial WETH balance: %s", initialBalance) + + depositAmount := eth.OneHundredthEther + + tx := user.Transfer(predeploys.WETHAddr, depositAmount) + receipt, err := tx.Included.Eval(ctx) + require.NoError(err) + require.Equal(types.ReceiptStatusSuccessful, receipt.Status) + t.Logf("Deposited %s ETH to WETH contract", depositAmount) + + finalBalance, err := contractio.Read(weth.BalanceOf(user.Address()), ctx) + require.NoError(err) + t.Logf("Final WETH balance: %s", finalBalance) + + expectedBalance := initialBalance.Add(depositAmount) + require.Equal(expectedBalance, finalBalance, "WETH balance should have increased by deposited amount") +} + +func TestSmokeTestFailure(gt *testing.T) { + t := devtest.SerialT(gt) + sys := presets.NewMinimal(t) + require := t.Require() + ctx := t.Ctx() + + user := sys.FunderL2.NewFundedEOA(eth.OneTenthEther) + + l2Client := sys.L2EL.Escape().EthClient() + weth := txib.NewBindings[txib.WETH]( + txib.WithClient(l2Client), + txib.WithTo(predeploys.WETHAddr), + txib.WithTest(t), + ) + + initialBalance, err := contractio.Read(weth.BalanceOf(user.Address()), ctx) + require.NoError(err) + t.Logf("Initial WETH balance: %s", initialBalance) + + depositAmount := eth.OneEther + + userBalance := user.GetBalance() + t.Logf("User balance: %s", userBalance) + + require.True(userBalance.Lt(depositAmount), "user should have insufficient funds for this transaction") + + t.Logf("user has insufficient funds: balance=%s, required=%s", userBalance, depositAmount) + + finalBalance, err := contractio.Read(weth.BalanceOf(user.Address()), ctx) + require.NoError(err) + t.Logf("Final WETH balance: %s", finalBalance) + + require.Equal(initialBalance, finalBalance, "WETH balance should not have changed") +}