Skip to content

Commit

Permalink
Merge pull request #264 from s29papi/enhancement/configurable-anvil-h…
Browse files Browse the repository at this point in the history
…osts

feat: Completed implementation of customizable anvil hosts
  • Loading branch information
jakim929 authored Nov 19, 2024
2 parents dfd9355 + 4cb6c03 commit 31bf6be
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 28 deletions.
7 changes: 3 additions & 4 deletions anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ var (
)

const (
host = "127.0.0.1"
anvilListeningLogStr = "Listening on"
)

Expand Down Expand Up @@ -77,7 +76,7 @@ func (a *Anvil) Start(ctx context.Context) error {
}

args := []string{
"--host", host,
"--host", a.cfg.Host,
"--accounts", fmt.Sprintf("%d", a.cfg.SecretsConfig.Accounts),
"--mnemonic", a.cfg.SecretsConfig.Mnemonic,
"--derivation-path", a.cfg.SecretsConfig.DerivationPath.String(),
Expand Down Expand Up @@ -241,11 +240,11 @@ func (a *Anvil) Stop(_ context.Context) error {
}

func (a *Anvil) Endpoint() string {
return fmt.Sprintf("http://%s:%d", host, a.cfg.Port)
return fmt.Sprintf("http://%s:%d", a.cfg.Host, a.cfg.Port)
}

func (a *Anvil) wsEndpoint() string {
return fmt.Sprintf("ws://%s:%d", host, a.cfg.Port)
return fmt.Sprintf("ws://%s:%d", a.cfg.Host, a.cfg.Port)
}

func (a *Anvil) Name() string {
Expand Down
12 changes: 11 additions & 1 deletion anvil/anvil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,17 @@ import (
)

func TestAnvil(t *testing.T) {
cfg := config.ChainConfig{ChainID: 10, Port: 0}
t.Run("set default host", func(t *testing.T) {
cfg := config.ChainConfig{ChainID: 10, Port: 0, Host: "127.0.0.1"}
testAnvilInstance(t, cfg)
})
t.Run("set custom host", func(t *testing.T) {
cfg := config.ChainConfig{ChainID: 10, Port: 0, Host: "0.0.0.0"}
testAnvilInstance(t, cfg)
})
}

func testAnvilInstance(t *testing.T, cfg config.ChainConfig) {
testlog := testlog.Logger(t, log.LevelInfo)

ctx, closeApp := context.WithCancelCause(context.Background())
Expand Down
1 change: 1 addition & 0 deletions config/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type L2Config struct {

type ChainConfig struct {
Name string
Host string
Port uint64
ChainID uint64

Expand Down
50 changes: 50 additions & 0 deletions config/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (

opservice "github.com/ethereum-optimism/optimism/op-service"

"net"
"regexp"

registry "github.com/ethereum-optimism/superchain-registry/superchain"

"github.com/urfave/cli/v2"
Expand All @@ -19,10 +22,12 @@ const (

L1ForkHeightFlagName = "l1.fork.height"
L1PortFlagName = "l1.port"
L1HostFlagName = "l1.host"

ChainsFlagName = "chains"
NetworkFlagName = "network"
L2StartingPortFlagName = "l2.starting.port"
L2HostFlagName = "l2.host"

LogsDirectoryFlagName = "logs.directory"

Expand Down Expand Up @@ -72,6 +77,18 @@ func BaseCLIFlags(envPrefix string) []cli.Flag {
Value: "",
EnvVars: opservice.PrefixEnvVar(envPrefix, "LOGS_DIRECTORY"),
},
&cli.StringFlag{
Name: L1HostFlagName,
Usage: "Host address for the L1 instance",
Value: "127.0.0.1",
EnvVars: opservice.PrefixEnvVar(envPrefix, "L1_HOST"),
},
&cli.StringFlag{
Name: L2HostFlagName,
Usage: "Host address for L2 instances",
Value: "127.0.0.1",
EnvVars: opservice.PrefixEnvVar(envPrefix, "L2_HOST"),
},
&cli.Uint64Flag{
Name: InteropDelayFlagName,
Value: 0, // enabled by default
Expand Down Expand Up @@ -132,6 +149,9 @@ type CLIConfig struct {
LogsDirectory string

ForkConfig *ForkCLIConfig

L1Host string
L2Host string
}

func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) {
Expand All @@ -145,6 +165,9 @@ func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) {
InteropDelay: ctx.Uint64(InteropDelayFlagName),

LogsDirectory: ctx.String(LogsDirectoryFlagName),

L1Host: ctx.String(L1HostFlagName),
L2Host: ctx.String(L2HostFlagName),
}

if ctx.Command.Name == ForkCommandName {
Expand All @@ -162,6 +185,13 @@ func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) {

// Check runs validatation on the cli configuration
func (c *CLIConfig) Check() error {
if err := validateHost(c.L1Host); err != nil {
return fmt.Errorf("invalid L1 host address: %w", err)
}
if err := validateHost(c.L2Host); err != nil {
return fmt.Errorf("invalid L2 host address: %w", err)
}

if c.ForkConfig != nil {
forkCfg := c.ForkConfig

Expand Down Expand Up @@ -194,3 +224,23 @@ func PrintDocLinks() {
fmt.Printf("\033]8;;%s\033\\%s\033]8;;\033\\\n", link.url, link.text)
}
}

func validateHost(host string) error {
if host == "" {
return fmt.Errorf("host cannot be empty")
}

if host == "localhost" || host == "127.0.0.1" {
return nil
}

if ip := net.ParseIP(host); ip != nil {
return nil
}

if matched, _ := regexp.MatchString(`^[a-zA-Z0-9][a-zA-Z0-9\-\.]+[a-zA-Z0-9]$`, host); !matched {
return fmt.Errorf("invalid host format: %s", host)
}

return nil
}
20 changes: 13 additions & 7 deletions opsimulator/opsimulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"net"
"net/http"
"strconv"
"strings"
"sync/atomic"

ophttp "github.com/ethereum-optimism/optimism/op-service/httputil"
Expand All @@ -33,7 +32,6 @@ import (
)

const (
host = "127.0.0.1"
l2NativeSuperchainERC20Addr = "0x420beeF000000000000000000000000000000001"
)

Expand All @@ -52,6 +50,7 @@ type OpSimulator struct {
bgTasksCancel context.CancelFunc
peers map[uint64]config.Chain

host string
port uint64
httpServer *ophttp.HTTPServer
crossL2Inbox *bindings.CrossL2Inbox
Expand All @@ -62,7 +61,7 @@ type OpSimulator struct {
}

// OpSimulator wraps around the l2 chain. By embedding `Chain`, it also implements the same inteface
func New(log log.Logger, closeApp context.CancelCauseFunc, port uint64, l1Chain, l2Chain config.Chain, peers map[uint64]config.Chain, interopDelay uint64) *OpSimulator {
func New(log log.Logger, closeApp context.CancelCauseFunc, port uint64, host string, l1Chain, l2Chain config.Chain, peers map[uint64]config.Chain, interopDelay uint64) *OpSimulator {
bgTasksCtx, bgTasksCancel := context.WithCancel(context.Background())

crossL2Inbox, err := bindings.NewCrossL2Inbox(predeploys.CrossL2InboxAddr, l2Chain.EthClient())
Expand All @@ -75,6 +74,7 @@ func New(log log.Logger, closeApp context.CancelCauseFunc, port uint64, l1Chain,

log: log.New("chain.id", l2Chain.Config().ChainID),
port: port,
host: host,
l1Chain: l1Chain,
crossL2Inbox: crossL2Inbox,
interopDelay: interopDelay,
Expand All @@ -96,20 +96,26 @@ func (opSim *OpSimulator) Start(ctx context.Context) error {
mux := http.NewServeMux()
mux.Handle("/", corsHandler(opSim.handler(ctx)))

hs, err := ophttp.StartHTTPServer(net.JoinHostPort(host, fmt.Sprintf("%d", opSim.port)), mux)
hs, err := ophttp.StartHTTPServer(net.JoinHostPort(opSim.host, fmt.Sprintf("%d", opSim.port)), mux)
if err != nil {
return fmt.Errorf("failed to start HTTP RPC server: %w", err)
}

cfg := opSim.Config()
opSim.log.Debug("started opsimulator", "name", cfg.Name, "chain.id", cfg.ChainID, "addr", hs.Addr())

opSim.httpServer = hs

if opSim.port == 0 {
opSim.port, err = strconv.ParseUint(strings.Split(hs.Addr().String(), ":")[1], 10, 64)
_, portStr, err := net.SplitHostPort(hs.Addr().String())
if err != nil {
panic(fmt.Errorf("failed to parse address: %w", err))
}

port, err := strconv.ParseUint(portStr, 10, 64)
if err != nil {
panic(fmt.Errorf("unexpected opsimulator listening port: %w", err))
}
opSim.port = port
}

ethClient, err := ethclient.Dial(opSim.Endpoint())
Expand Down Expand Up @@ -443,7 +449,7 @@ func (opSim *OpSimulator) Config() *config.ChainConfig {

// Overridden such that the correct port is used
func (opSim *OpSimulator) Endpoint() string {
return fmt.Sprintf("http://%s:%d", host, opSim.port)
return fmt.Sprintf("http://%s:%d", opSim.host, opSim.port)
}

func corsHandler(next http.Handler) http.Handler {
Expand Down
5 changes: 3 additions & 2 deletions orchestrator/orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkCo
// Sping up OpSim to fornt the L2 instances
for i := range networkConfig.L2Configs {
cfg := networkConfig.L2Configs[i]
l2OpSims[cfg.ChainID] = opsimulator.New(log, closeApp, nextL2Port, l1Anvil, l2Anvils[cfg.ChainID], l2Anvils, networkConfig.InteropDelay)

l2OpSims[cfg.ChainID] = opsimulator.New(log, closeApp, nextL2Port, cfg.Host, l1Anvil, l2Anvils[cfg.ChainID], l2Anvils, networkConfig.InteropDelay)

// only increment expected port if it has been specified
if nextL2Port > 0 {
Expand All @@ -72,11 +73,11 @@ func NewOrchestrator(log log.Logger, closeApp context.CancelCauseFunc, networkCo

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

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

for _, chain := range o.l2Chains {
if err := chain.Start(ctx); err != nil {
return fmt.Errorf("l2 chain %s failed to start: %w", chain.Config().Name, err)
Expand Down
56 changes: 42 additions & 14 deletions orchestrator/orchestrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,9 @@ import (
"testing"
"time"

"github.com/ethereum-optimism/supersim/config"

"github.com/ethereum-optimism/optimism/op-service/testlog"

"github.com/ethereum-optimism/supersim/config"
"github.com/ethereum/go-ethereum/log"

"github.com/stretchr/testify/require"
)

Expand All @@ -20,12 +17,12 @@ type TestSuite struct {
orchestrator *Orchestrator
}

func createTestSuite(t *testing.T) *TestSuite {
networkConfig := config.GetDefaultNetworkConfig(uint64(time.Now().Unix()), "")
func createTestSuite(t *testing.T, networkConfig *config.NetworkConfig) *TestSuite {
testlog := testlog.Logger(t, log.LevelInfo)

ctx, closeApp := context.WithCancelCause(context.Background())
orchestrator, _ := NewOrchestrator(testlog, closeApp, &networkConfig)
orchestrator, _ := NewOrchestrator(testlog, closeApp, networkConfig)

t.Cleanup(func() {
closeApp(nil)
if err := orchestrator.Stop(context.Background()); err != nil {
Expand All @@ -42,23 +39,54 @@ func createTestSuite(t *testing.T) *TestSuite {
}

func TestStartup(t *testing.T) {
testSuite := createTestSuite(t)
networkConfig := config.GetDefaultNetworkConfig(uint64(time.Now().Unix()), "")
networkConfig.L1Config.Host = "127.0.0.1"
for i := range networkConfig.L2Configs {
networkConfig.L2Configs[i].Host = "127.0.0.1"
}
testSuite := createTestSuite(t, &networkConfig)
verifyChainConfigs(t, testSuite)
}

func TestCustomHosts(t *testing.T) {
networkConfig := config.GetDefaultNetworkConfig(uint64(time.Now().Unix()), "")
networkConfig.L1Config.Host = "0.0.0.0"
for i := range networkConfig.L2Configs {
networkConfig.L2Configs[i].Host = "0.0.0.0"
}
testSuite := createTestSuite(t, &networkConfig)
verifyChainConfigs(t, testSuite)
}
func TestMixedCustomHosts(t *testing.T) {
networkConfig := config.GetDefaultNetworkConfig(uint64(time.Now().Unix()), "")
networkConfig.L1Config.Host = "127.0.0.1"
for i := range networkConfig.L2Configs {
networkConfig.L2Configs[i].Host = "0.0.0.0"
}
testSuite := createTestSuite(t, &networkConfig)
verifyChainConfigs(t, testSuite)
}

require.Equal(t, testSuite.orchestrator.L1Chain().Config().ChainID, uint64(900))
require.Nil(t, testSuite.orchestrator.L1Chain().Config().L2Config)
func verifyChainConfigs(t *testing.T, suite *TestSuite) {
// Verify L1 chain
require.Equal(t, uint64(900), suite.orchestrator.L1Chain().Config().ChainID)
require.Nil(t, suite.orchestrator.L1Chain().Config().L2Config)

chains := testSuite.orchestrator.L2Chains()
require.Equal(t, len(chains), 2)
// Verify L2 chains
chains := suite.orchestrator.L2Chains()
require.Equal(t, 2, len(chains))

// Verify first L2 chain
require.True(t, slices.ContainsFunc(chains, func(chain config.Chain) bool {
return chain.Config().ChainID == uint64(901)
}))
require.NotNil(t, chains[0].Config().L2Config)
require.Equal(t, chains[0].Config().L2Config.L1ChainID, uint64(900))
require.Equal(t, uint64(900), chains[0].Config().L2Config.L1ChainID)

// Verify second L2 chain
require.True(t, slices.ContainsFunc(chains, func(chain config.Chain) bool {
return chain.Config().ChainID == uint64(902)
}))
require.NotNil(t, chains[1].Config().L2Config)
require.Equal(t, chains[1].Config().L2Config.L1ChainID, uint64(900))
require.Equal(t, uint64(900), chains[1].Config().L2Config.L1ChainID)
}
6 changes: 6 additions & 0 deletions supersim.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ func NewSupersim(log log.Logger, envPrefix string, closeApp context.CancelCauseF
networkConfig.L1Config.Port = cliConfig.L1Port
networkConfig.L2StartingPort = cliConfig.L2StartingPort

// Forward host config
networkConfig.L1Config.Host = cliConfig.L1Host
for i := range networkConfig.L2Configs {
networkConfig.L2Configs[i].Host = cliConfig.L2Host
}

// Forward interop config
networkConfig.InteropAutoRelay = cliConfig.InteropAutoRelay
networkConfig.InteropDelay = cliConfig.InteropDelay
Expand Down

0 comments on commit 31bf6be

Please sign in to comment.