Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

supersim testsuite creator #34

Merged
merged 2 commits into from
Jul 10, 2024
Merged
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
5 changes: 2 additions & 3 deletions anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ func (a *Anvil) Start(ctx context.Context) error {
return errors.New("anvil already started")
}

anvilLog := a.log.New("chain.id", a.cfg.ChainId)
anvilLog.Info("starting anvil")

tempFile, err := os.CreateTemp("", "genesis-*.json")
if err != nil {
return fmt.Errorf("error creating temporary genesis file: %w", err)
Expand All @@ -79,6 +76,8 @@ func (a *Anvil) Start(ctx context.Context) error {
"--init", tempFile.Name(),
}

anvilLog := a.log.New("role", "anvil", "chain.id", a.cfg.ChainId)
anvilLog.Info("starting anvil", "args", args)
a.cmd = exec.CommandContext(a.resourceCtx, "anvil", args...)
go func() {
<-ctx.Done()
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ go 1.22.3
require (
github.com/ethereum-optimism/optimism v1.7.7
github.com/ethereum/go-ethereum v1.13.15
github.com/stretchr/testify v1.9.0
github.com/urfave/cli/v2 v2.27.1
)

require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240614103325-d8902381f5d8 // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
Expand Down
84 changes: 46 additions & 38 deletions supersim.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import (
"context"

"github.com/ethereum-optimism/supersim/anvil"
op_simulator "github.com/ethereum-optimism/supersim/op-simulator"
opsim "github.com/ethereum-optimism/supersim/op-simulator"

"github.com/ethereum/go-ethereum/log"
)

type Config struct {
l1Chain Chain
l2Chains []Chain
l1Chain ChainConfig
l2Chains []ChainConfig
}

type Chain struct {
type ChainConfig struct {
anvilConfig anvil.Config
opSimConfig op_simulator.Config
opSimConfig opsim.Config
}

//go:embed genesis/genesis-l1.json
Expand All @@ -31,32 +31,46 @@ var genesisL1JSON []byte
var genesisL2JSON []byte

var DefaultConfig = Config{
l1Chain: Chain{anvilConfig: anvil.Config{ChainId: 1, Port: 8545, Genesis: genesisL1JSON}, opSimConfig: op_simulator.Config{Port: 8546}},
l2Chains: []Chain{{anvilConfig: anvil.Config{ChainId: 10, Port: 9545, Genesis: genesisL2JSON}, opSimConfig: op_simulator.Config{Port: 9546}}, {anvilConfig: anvil.Config{ChainId: 30, Port: 9555, Genesis: genesisL2JSON}, opSimConfig: op_simulator.Config{Port: 9556}}},
l1Chain: ChainConfig{
anvilConfig: anvil.Config{ChainId: 1, Port: 8545, Genesis: genesisL1JSON},
opSimConfig: opsim.Config{Port: 8546},
},
l2Chains: []ChainConfig{
{
anvilConfig: anvil.Config{ChainId: 10, Port: 9545, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 9546},
},
{
anvilConfig: anvil.Config{ChainId: 30, Port: 9555, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 9556},
},
},
}

type Supersim struct {
log log.Logger

l1Anvil *anvil.Anvil
l1Anvil *anvil.Anvil
l1OpSim *opsim.OpSimulator

l2Anvils map[uint64]*anvil.Anvil
l1OpSim *op_simulator.OpSimulator
l2OpSims map[uint64]*op_simulator.OpSimulator
l2OpSims map[uint64]*opsim.OpSimulator
}

func NewSupersim(log log.Logger, config *Config) *Supersim {
l1Anvil := anvil.New(log, &config.l1Chain.anvilConfig)
l1OpSim := op_simulator.New(log, &config.l1Chain.opSimConfig, l1Anvil)
l1OpSim := opsim.New(log, &config.l1Chain.opSimConfig, l1Anvil)

l2Anvils := make(map[uint64]*anvil.Anvil)
l2OpSims := make(map[uint64]*op_simulator.OpSimulator)
for _, l2ChainConfig := range config.l2Chains {
l2OpSims := make(map[uint64]*opsim.OpSimulator)
for i := range config.l2Chains {
l2ChainConfig := config.l2Chains[i]
l2Anvil := anvil.New(log, &l2ChainConfig.anvilConfig)
l2Anvils[l2ChainConfig.anvilConfig.ChainId] = l2Anvil
l2OpSims[l2ChainConfig.anvilConfig.ChainId] = op_simulator.New(log, &l2ChainConfig.opSimConfig, l2Anvil)
l2OpSims[l2ChainConfig.anvilConfig.ChainId] = opsim.New(log, &l2ChainConfig.opSimConfig, l2Anvil)
}

return &Supersim{log, l1Anvil, l2Anvils, l1OpSim, l2OpSims}
return &Supersim{log, l1Anvil, l1OpSim, l2Anvils, l2OpSims}
}

func (s *Supersim) Start(ctx context.Context) error {
Expand All @@ -65,17 +79,15 @@ func (s *Supersim) Start(ctx context.Context) error {
if err := s.l1Anvil.Start(ctx); err != nil {
return fmt.Errorf("l1 anvil failed to start: %w", err)
}
if err := s.l1OpSim.Start(ctx); err != nil {
return fmt.Errorf("l1 op simulator failed to start: %w", err)
}

for _, l2Anvil := range s.l2Anvils {
if err := l2Anvil.Start(ctx); err != nil {
return fmt.Errorf("l2 anvil failed to start: %w", err)
}
}

if err := s.l1OpSim.Start(ctx); err != nil {
return fmt.Errorf("l1 op simulator failed to start: %w", err)
}

for _, l2OpSim := range s.l2OpSims {
if err := l2OpSim.Start(ctx); err != nil {
return fmt.Errorf("l2 op simulator failed to start: %w", err)
Expand All @@ -97,32 +109,35 @@ func (s *Supersim) Start(ctx context.Context) error {
func (s *Supersim) Stop(ctx context.Context) error {
s.log.Info("stopping supersim")

for _, l2OpSim := range s.l2OpSims {
if err := l2OpSim.Stop(ctx); err != nil {
return fmt.Errorf("l2 op simulator failed to stop: %w", err)
}
s.log.Info("stopped op simulator", "chain.id", l2OpSim.ChainId())
}
for _, l2Anvil := range s.l2Anvils {
if err := l2Anvil.Stop(); err != nil {
return fmt.Errorf("l2 anvil failed to stop: %w", err)
}
}

if err := s.l1Anvil.Stop(); err != nil {
return fmt.Errorf("l1 anvil failed to stop: %w", err)
}

if err := s.l1OpSim.Stop(ctx); err != nil {
return fmt.Errorf("l1 op simulator failed to stop: %w", err)
}
s.log.Info("Stopped op simulator", "chain.id", s.l1OpSim.ChainId())

for _, l2OpSim := range s.l2OpSims {
if err := l2OpSim.Stop(ctx); err != nil {
return fmt.Errorf("l2 op simulator failed to stop: %w", err)
}
s.log.Info("Stopped op simulator", "chain.id", l2OpSim.ChainId())
if err := s.l1Anvil.Stop(); err != nil {
return fmt.Errorf("l1 anvil failed to stop: %w", err)
}
s.log.Info("stopped op simulator", "chain.id", s.l1OpSim.ChainId())

return nil
}

func (s *Supersim) Stopped() bool {
for _, l2OpSim := range s.l2OpSims {
if stopped := l2OpSim.Stopped(); !stopped {
return stopped
}
}
for _, l2Anvil := range s.l2Anvils {
if stopped := l2Anvil.Stopped(); !stopped {
return stopped
Expand All @@ -132,13 +147,6 @@ func (s *Supersim) Stopped() bool {
if stopped := s.l1Anvil.Stopped(); !stopped {
return stopped
}

for _, l2OpSim := range s.l2OpSims {
if stopped := l2OpSim.Stopped(); !stopped {
return stopped
}
}

if stopped := s.l1OpSim.Stopped(); !stopped {
return stopped
}
Expand Down
112 changes: 67 additions & 45 deletions supersim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package supersim

import (
"context"
"fmt"
"log"
"os"
"testing"
"time"

oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/stretchr/testify/require"

"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)

Expand All @@ -21,57 +21,79 @@ const (
l1BlockAddress = "0x4200000000000000000000000000000000000015"
)

func TestGenesisState(t *testing.T) {
logger := oplog.NewLogger(os.Stderr, oplog.DefaultCLIConfig())
supersim := NewSupersim(logger, &DefaultConfig)
err := supersim.Start(context.Background())

defer func() {
err := supersim.Stop(context.Background())
if err != nil {
t.Fatalf("Failed to stop supersim: %v", err)
type TestSuite struct {
t *testing.T

Cfg *Config
Supersim *Supersim
Comment on lines +27 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason that these fields should be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now they could technically be private. I made them public in the even this test suite is used in a diff package

}

func createTestSuite(t *testing.T) *TestSuite {
cfg := &DefaultConfig

testlog := testlog.Logger(t, log.LevelInfo)
supersim := NewSupersim(testlog, cfg)
t.Cleanup(func() {
if err := supersim.Stop(context.Background()); err != nil {
t.Errorf("failed to stop supersim: %s", err)
}
}()
})

if err != nil {
t.Fatalf("Failed to start supersim: %v", err)
if err := supersim.Start(context.Background()); err != nil {
t.Fatalf("unable to start supersim: %s", err)
return nil
}

for _, l2ChainConfig := range DefaultConfig.l2Chains {
rpcUrl := fmt.Sprintf("http://127.0.0.1:%d", l2ChainConfig.opSimConfig.Port)
client, clientCreateErr := rpc.Dial(rpcUrl)
return &TestSuite{
t: t,
Cfg: cfg,
Supersim: supersim,
}
}

if clientCreateErr != nil {
t.Fatalf("Failed to create client: %v", clientCreateErr)
}
func TestStartup(t *testing.T) {
testSuite := createTestSuite(t)

defer client.Close()
var chainId math.HexOrDecimal64

var code string
err := client.CallContext(context.Background(), &code, "eth_getCode", crossL2InboxAddress, "latest")
if err != nil {
log.Fatalf("Failed to get code: %v", err)
}
// test that all chains can be queried
l1Client, err := rpc.Dial(testSuite.Supersim.l1OpSim.Endpoint())
require.NoError(t, err)
require.NoError(t, l1Client.CallContext(context.Background(), &chainId, "eth_chainId"))
require.Equal(t, uint64(chainId), testSuite.Supersim.l1OpSim.ChainId())
l1Client.Close()

if code == emptyCode {
t.Error("CrossL2Inbox is not deployed")
}
for id, l2Chain := range testSuite.Supersim.l2OpSims {
require.Equal(t, id, l2Chain.ChainId())

err = client.CallContext(context.Background(), &code, "eth_getCode", l2toL2CrossDomainMessengerAddress, "latest")
if err != nil {
log.Fatalf("Failed to get code: %v", err)
}
if code == emptyCode {
t.Error("L2toL2CrossDomainMessenger is not deployed")
}
l2Client, err := rpc.Dial(l2Chain.Endpoint())
require.NoError(t, err)
require.NoError(t, l2Client.CallContext(context.Background(), &chainId, "eth_chainId"))

err = client.CallContext(context.Background(), &code, "eth_getCode", l1BlockAddress, "latest")
if err != nil {
log.Fatalf("Failed to get code: %v", err)
}
if code == emptyCode {
t.Error("L1Block is not deployed")
}
// Commented out due to a bug in foundry that sets the chain id to 1 whenever genesis.json file is supplied
//require.Equal(t, l2Chain.ChainId(), uint64(chainId))

l2Client.Close()
}
}

func TestGenesisState(t *testing.T) {
testSuite := createTestSuite(t)

// assert that the predeploys exists on the l2 anvil instances
for _, l2Chain := range testSuite.Supersim.l2OpSims {
client, err := rpc.Dial(l2Chain.Endpoint())
require.NoError(t, err)
defer client.Close()

var code string
require.NoError(t, client.CallContext(context.Background(), &code, "eth_getCode", crossL2InboxAddress, "latest"))
require.NotEqual(t, emptyCode, code, "CrossL2Inbox is not deployed")

require.NoError(t, client.CallContext(context.Background(), &code, "eth_getCode", l2toL2CrossDomainMessengerAddress, "latest"))
require.NotEqual(t, emptyCode, code, "L2ToL2CrosSDomainMessenger is not deployed")

require.NoError(t, client.CallContext(context.Background(), &code, "eth_getCode", l1BlockAddress, "latest"))
require.NotEqual(t, emptyCode, code, "L1Block is not deployed")
}
}
Loading