Skip to content

Commit

Permalink
feat(orchestrator): create orchestrator package
Browse files Browse the repository at this point in the history
  • Loading branch information
tremarkley committed Jul 10, 2024
1 parent 13aa0ec commit 2c41ef0
Show file tree
Hide file tree
Showing 7 changed files with 252 additions and 175 deletions.
8 changes: 7 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,11 @@ func SupersimMain(ctx *cli.Context, closeApp context.CancelCauseFunc) (cliapp.Li
}

// use config and setup supersim
return supersim.NewSupersim(log, &supersim.DefaultConfig), nil
s, err := supersim.NewSupersim(log, &supersim.DefaultConfig)

if err != nil {
return nil, fmt.Errorf("failed to create supersim")
}

return s, nil
}
7 changes: 6 additions & 1 deletion op-simulator/op_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const (
)

type Config struct {
Port uint64
Port uint64
BaseChainId uint64
}

type OpSimulator struct {
Expand Down Expand Up @@ -107,3 +108,7 @@ func (opSim *OpSimulator) Endpoint() string {
func (opSim *OpSimulator) ChainId() uint64 {
return opSim.anvil.ChainId()
}

func (opSim *OpSimulator) BaseChainId() uint64 {
return opSim.cfg.BaseChainId
}
File renamed without changes.
File renamed without changes.
213 changes: 213 additions & 0 deletions orchestrator/orchestrator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package orchestrator

import (
"context"
_ "embed"
"fmt"
"strings"
"sync"

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

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

type ChainConfig struct {
Port uint64
ChainId uint64
// The base chain ID when the chain is a rollup
// If set to 0 or the current chain's ID, the chain is considered an L1 chain
BaseChainId uint64
}

type OrchestratorConfig struct {
ChainConfigs []ChainConfig
}

type Orchestrator struct {
log log.Logger

OpSimInstances []*op_simulator.OpSimulator
anvilInstances []*anvil.Anvil
}

//go:embed genesisstates/genesis-l1.json
var genesisL1JSON []byte

//go:embed genesisstates/genesis-l2.json
var genesisL2JSON []byte

func NewOrchestrator(log log.Logger, config *OrchestratorConfig) (*Orchestrator, error) {
var opSimInstances []*op_simulator.OpSimulator
var anvilInstances []*anvil.Anvil

l1Count := 0
for _, config := range config.ChainConfigs {
if config.BaseChainId == 0 {
l1Count++
}
}

if l1Count > 1 {
return nil, fmt.Errorf("supersim does not support more than one l1")
}

for _, chainConfig := range config.ChainConfigs {
var genesis []byte
if chainConfig.BaseChainId == 0 {
genesis = genesisL1JSON
} else {
genesis = genesisL2JSON
}
anvil := anvil.New(log, &anvil.Config{ChainId: chainConfig.ChainId, Genesis: genesis})
anvilInstances = append(anvilInstances, anvil)
opSimInstances = append(opSimInstances, op_simulator.New(log, &op_simulator.Config{Port: chainConfig.Port, BaseChainId: chainConfig.BaseChainId}, anvil))
}

return &Orchestrator{log, opSimInstances, anvilInstances}, nil
}

func (o *Orchestrator) Start(ctx context.Context) error {
o.log.Info("starting orchestrator")

for _, anvilInstance := range o.anvilInstances {
if err := anvilInstance.Start(ctx); err != nil {
return fmt.Errorf("anvil instance chain.id=%v failed to start: %w", anvilInstance.ChainId(), err)
}
}
for _, opSimInstance := range o.OpSimInstances {
if err := opSimInstance.Start(ctx); err != nil {
return fmt.Errorf("op simulator instance chain.id=%v failed to start: %w", opSimInstance.ChainId(), err)
}
}

if err := o.WaitUntilReady(); err != nil {
return fmt.Errorf("orchestrator failed to get ready: %w", err)
}

o.enableAnvilLogging()

o.log.Info("orchestrator is ready")

return nil
}

func (s *Orchestrator) Stop(ctx context.Context) error {
s.log.Info("stopping orchestrator")

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

s.log.Info("stopped orchestrator")

return nil
}

func (o *Orchestrator) Stopped() bool {
for _, anvil := range o.anvilInstances {
if stopped := anvil.Stopped(); !stopped {
return stopped
}
}
for _, opSim := range o.OpSimInstances {
if stopped := opSim.Stopped(); !stopped {
return stopped
}
}

return true
}

func (s *Orchestrator) WaitUntilReady() error {
var once sync.Once
var err error
ctx, cancel := context.WithCancel(context.Background())

handleErr := func(e error) {
if e != nil {
once.Do(func() {
err = e
cancel()
})
}
}

var wg sync.WaitGroup

waitForAnvil := func(anvil *anvil.Anvil) {
defer wg.Done()
handleErr(anvil.WaitUntilReady(ctx))
}

s.iterateAnvilInstances(func(chain *anvil.Anvil) {
wg.Add(1)
go waitForAnvil(chain)
})

wg.Wait()

return err
}

func (o *Orchestrator) iterateAnvilInstances(fn func(anvil *anvil.Anvil)) {
for _, anvilInstance := range o.anvilInstances {
fn(anvilInstance)
}
}

func (o *Orchestrator) enableAnvilLogging() {
o.iterateAnvilInstances(func(anvil *anvil.Anvil) {
anvil.EnableLogging()
})
}

func (o *Orchestrator) L1OpSimulator() *op_simulator.OpSimulator {
var result *op_simulator.OpSimulator
for _, opSim := range o.OpSimInstances {
if opSim.BaseChainId() == 0 {
result = opSim
}
}

return result
}

func (o *Orchestrator) L2OpSimulators() []*op_simulator.OpSimulator {
var result []*op_simulator.OpSimulator
for _, opSim := range o.OpSimInstances {
if opSim.BaseChainId() != 0 {
result = append(result, opSim)
}
}
return result
}

func (o *Orchestrator) ConfigAsString() string {
var b strings.Builder

fmt.Fprintf(&b, "\nSupersim Config:\n")
if o.L1OpSimulator() != nil {
fmt.Fprintf(&b, "L1:\n")
fmt.Fprintf(&b, " Chain ID: %d RPC: %s\n", o.L1OpSimulator().ChainId(), o.L1OpSimulator().Endpoint())
}

if len(o.L2OpSimulators()) > 0 {
fmt.Fprintf(&b, "L2:\n")
for _, opSim := range o.L2OpSimulators() {
fmt.Fprintf(&b, " Chain ID: %d RPC: %s\n", opSim.ChainId(), opSim.Endpoint())
}
}

return b.String()
}
Loading

0 comments on commit 2c41ef0

Please sign in to comment.