-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(orchestrator): create orchestrator package
- Loading branch information
1 parent
13aa0ec
commit 2c41ef0
Showing
7 changed files
with
252 additions
and
175 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
Oops, something went wrong.