From d90bd4f0811a4f5b7ff3d8ee1d672c4f90fe7e57 Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Tue, 9 Jul 2024 14:17:28 -0400 Subject: [PATCH] starting anvil with port 0 --- anvil/anvil.go | 57 +++++++++++++++++++++++++++++++++------------ anvil/anvil_test.go | 32 +++++++++++++++++++++++++ supersim.go | 4 ++-- 3 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 anvil/anvil_test.go diff --git a/anvil/anvil.go b/anvil/anvil.go index 46f2bab7..067e12ab 100644 --- a/anvil/anvil.go +++ b/anvil/anvil.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "os/exec" + "strconv" "strings" "sync/atomic" "time" @@ -36,7 +37,8 @@ type Anvil struct { } const ( - host = "127.0.0.1" + host = "127.0.0.1" + anvilListeningLogStr = "Listening on" ) func New(log log.Logger, cfg *Config) *Anvil { @@ -58,22 +60,24 @@ func (a *Anvil) Start(ctx context.Context) error { 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) - } - - _, err = tempFile.Write(a.cfg.Genesis) - if err != nil { - return fmt.Errorf("error writing to genesis file: %w", err) - } - // Prep args args := []string{ "--host", host, "--chain-id", fmt.Sprintf("%d", a.cfg.ChainId), "--port", fmt.Sprintf("%d", a.cfg.Port), - "--init", tempFile.Name(), + } + + if len(a.cfg.Genesis) > 0 { + tempFile, err := os.CreateTemp("", "genesis-*.json") + if err != nil { + return fmt.Errorf("error creating temporary genesis file: %w", err) + } + if _, err = tempFile.Write(a.cfg.Genesis); err != nil { + return fmt.Errorf("error writing to genesis file: %w", err) + } + + defer os.Remove(tempFile.Name()) + args = append(args, "--init", tempFile.Name()) } a.cmd = exec.CommandContext(a.resourceCtx, "anvil", args...) @@ -82,6 +86,10 @@ func (a *Anvil) Start(ctx context.Context) error { a.resourceCancel() }() + // In the event anvil is started with port 0, we'll need to block + // and see what port anvil eventually binds to when started + anvilPortCh := make(chan uint64) + // Handle stdout/stderr // - TODO: Figure out best way to dump into logger. Some hex isn't showing appropriately stdout, err := a.cmd.StdoutPipe() @@ -95,7 +103,17 @@ func (a *Anvil) Start(ctx context.Context) error { go func() { scanner := bufio.NewScanner(stdout) for scanner.Scan() { - anvilLog.Info(scanner.Text()) + txt := scanner.Text() + anvilLog.Info(txt) + + // scan for port if applicable + if a.cfg.Port == 0 && strings.HasPrefix(txt, anvilListeningLogStr) { + port, err := strconv.ParseInt(strings.Split(txt, ":")[1], 10, 64) + if err != nil { + panic(fmt.Errorf("unexpected anvil listening port log: %w", err)) + } + anvilPortCh <- uint64(port) + } } }() go func() { @@ -111,8 +129,6 @@ func (a *Anvil) Start(ctx context.Context) error { } go func() { - defer os.Remove(tempFile.Name()) - if err := a.cmd.Wait(); err != nil { anvilLog.Error("anvil terminated with an error", "error", err) } else { @@ -121,6 +137,17 @@ func (a *Anvil) Start(ctx context.Context) error { a.stoppedCh <- struct{}{} }() + // wait & update the port if applicable. Since we're in the same routine to which `Start` + // is called, we're safe to overrwrite the `Port` field which the caller can observe + if a.cfg.Port == 0 { + done := ctx.Done() + select { + case a.cfg.Port = <-anvilPortCh: + case <-done: + return ctx.Err() + } + } + return nil } diff --git a/anvil/anvil_test.go b/anvil/anvil_test.go new file mode 100644 index 00000000..90e213cf --- /dev/null +++ b/anvil/anvil_test.go @@ -0,0 +1,32 @@ +package anvil + +import ( + "context" + "testing" + + "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" +) + +func TestAnvil(t *testing.T) { + cfg := Config{ChainId: 10, Port: 0} + testlog := testlog.Logger(t, log.LevelInfo) + anvil := New(testlog, &cfg) + + require.NoError(t, anvil.Start(context.Background())) + + // port overriden on startup + require.NotEqual(t, cfg.Port, 0) + + client, err := rpc.Dial(anvil.Endpoint()) + require.NoError(t, err) + + // query chainId + var chainId math.HexOrDecimal64 + require.NoError(t, client.CallContext(context.Background(), &chainId, "eth_chainId")) + require.Equal(t, uint64(chainId), cfg.ChainId) +} diff --git a/supersim.go b/supersim.go index ab5f6f0f..07031f27 100644 --- a/supersim.go +++ b/supersim.go @@ -27,8 +27,8 @@ 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}, + {ChainId: 10, Port: 0, Genesis: genesisL2JSON}, + {ChainId: 30, Port: 0, Genesis: genesisL2JSON}, }, }