Skip to content

Commit

Permalink
supersim testsuite creator (#34)
Browse files Browse the repository at this point in the history
* test suite creator

* nits
  • Loading branch information
hamdiallam authored Jul 10, 2024
1 parent db0e244 commit d7153fc
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 86 deletions.
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
}

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")
}
}

0 comments on commit d7153fc

Please sign in to comment.