Skip to content

Commit

Permalink
feat: create op-simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
tremarkley committed Jul 8, 2024
1 parent a209230 commit 91ceacb
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 22 deletions.
4 changes: 4 additions & 0 deletions anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,7 @@ func (a *Anvil) Stop() error {
func (a *Anvil) Stopped() bool {
return a.stopped.Load()
}

func (a *Anvil) Endpoint() string {
return fmt.Sprintf("http://%s:%d", host, a.cfg.Port)
}
90 changes: 90 additions & 0 deletions op-simulator/op_simulator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package op_simulator

import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"sync/atomic"

ophttp "github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum-optimism/supersim/anvil"
"github.com/ethereum/go-ethereum/log"
)

type Config struct {
Port uint64
}

type OpSimulator struct {
Log log.Logger
Anvil *anvil.Anvil
HttpServer *ophttp.HTTPServer

stopped atomic.Bool

cfg *Config
}

const (
host = "127.0.0.1"
)

func New(log log.Logger, cfg *Config, anvil *anvil.Anvil) *OpSimulator {
return &OpSimulator{
Log: log,
cfg: cfg,
Anvil: anvil,
}
}

func (opSim *OpSimulator) Start(ctx context.Context) error {
proxy, err := opSim.createReverseProxy()
if err != nil {
return fmt.Errorf("Error creating reverse proxy: %w", err)
}
mux := http.NewServeMux()
mux.Handle("/", proxy)
endpoint := net.JoinHostPort(host, strconv.Itoa(int(opSim.cfg.Port)))
opSim.Log.Info("Starting simulator", "endpoint", endpoint)

hs, err := ophttp.StartHTTPServer(endpoint, mux)
if err != nil {
return fmt.Errorf("failed to start HTTP RPC server: %w", err)
}
opSim.HttpServer = hs

return nil
}

func (opSim *OpSimulator) Stop(ctx context.Context) error {
if opSim.stopped.Load() {
return errors.New("already stopped")
}
if !opSim.stopped.CompareAndSwap(false, true) {
return nil // someone else stopped
}

return opSim.HttpServer.Stop(ctx)
}

func (a *OpSimulator) Stopped() bool {
return a.stopped.Load()
}

func (opSim *OpSimulator) createReverseProxy() (*httputil.ReverseProxy, error) {
targetURL, err := url.Parse(opSim.Anvil.Endpoint())
if err != nil {
return nil, fmt.Errorf("failed to parse target URL: %w", err)
}
proxy := &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.SetURL(targetURL)
},
}
return proxy, nil
}
95 changes: 74 additions & 21 deletions supersim.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ import (
"context"

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

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

type Config struct {
l1Chain anvil.Config
l2Chains []anvil.Config
l1Chain Chain
l2Chains []Chain
}

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

//go:embed genesis/genesis-l1.json
Expand All @@ -23,63 +29,110 @@ var genesisL1JSON []byte
var genesisL2JSON []byte

var DefaultConfig = Config{
l1Chain: anvil.Config{ChainId: 1, Port: 8545, Genesis: genesisL1JSON},
l2Chains: []anvil.Config{
{ChainId: 10, Port: 9545, Genesis: genesisL2JSON},
{ChainId: 30, Port: 9555, Genesis: genesisL2JSON},
},
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}}},
}

type Supersim struct {
log log.Logger

l1Chain *anvil.Anvil
l2Chains map[uint64]*anvil.Anvil
l1Anvil *anvil.Anvil
l2Anvils map[uint64]*anvil.Anvil
l1OpSim *op_simulator.OpSimulator
l2OpSims map[uint64]*op_simulator.OpSimulator
}

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

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

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

func (s *Supersim) Start(ctx context.Context) error {
s.log.Info("starting supersim")

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

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

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

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

return nil
}

func (s *Supersim) Stop(_ context.Context) error {
func (s *Supersim) Stop(ctx context.Context) error {
s.log.Info("stopping supersim")

for _, l2Chain := range s.l2Chains {
if err := l2Chain.Stop(); err != nil {
for _, l2Anvil := range s.l2Anvils {
if err := l2Anvil.Stop(); err != nil {
return fmt.Errorf("l2 chain failed to stop: %w", err)
}
}

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

if err := s.l1OpSim.Stop(ctx); err != nil {
return fmt.Errorf("l1 op sim failed to stop: %w", err)
} else {
s.l1OpSim.Log.Info("Stopped op simulator", "endpoint", s.l1OpSim.HttpServer.Addr().String())
}

for _, l2OpSim := range s.l2OpSims {
if err := l2OpSim.Stop(ctx); err != nil {
return fmt.Errorf("l2 op sim failed to stop: %w", err)
} else {
l2OpSim.Log.Info("Stopped op simulator", "endpoint", l2OpSim.HttpServer.Addr().String())
}
}

return nil
}

func (s *Supersim) Stopped() bool {
return s.l1Chain.Stopped()
for _, l2Anvil := range s.l2Anvils {
if stopped := l2Anvil.Stopped(); stopped != true {

Check failure on line 118 in supersim.go

View workflow job for this annotation

GitHub Actions / go-lint

S1002: should omit comparison to bool constant, can be simplified to `!stopped` (gosimple)
return stopped
}
}

if stopped := s.l1Anvil.Stopped(); stopped != true {

Check failure on line 123 in supersim.go

View workflow job for this annotation

GitHub Actions / go-lint

S1002: should omit comparison to bool constant, can be simplified to `!stopped` (gosimple)
return stopped
}

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

Check failure on line 128 in supersim.go

View workflow job for this annotation

GitHub Actions / go-lint

S1002: should omit comparison to bool constant, can be simplified to `!stopped` (gosimple)
return stopped
}
}

if stopped := s.l1OpSim.Stopped(); stopped != true {
return stopped
}

return true
}
2 changes: 1 addition & 1 deletion supersim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestGenesisState(t *testing.T) {
}()

for _, l2ChainConfig := range DefaultConfig.l2Chains {
client, err := utils.WaitForAnvilClientToBeReady(fmt.Sprintf("http://127.0.0.1:%d", l2ChainConfig.Port), anvilClientTimeout)
client, err := utils.WaitForAnvilClientToBeReady(fmt.Sprintf("http://127.0.0.1:%d", l2ChainConfig.opSimConfig.Port), anvilClientTimeout)
if err != nil {
t.Fatalf("Failed to connect to RPC server: %v", err)
}
Expand Down

0 comments on commit 91ceacb

Please sign in to comment.