Skip to content

Commit

Permalink
starting anvil with port 0
Browse files Browse the repository at this point in the history
  • Loading branch information
hamdiallam committed Jul 10, 2024
1 parent d12c18b commit 76d432e
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 34 deletions.
62 changes: 42 additions & 20 deletions anvil/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"sync/atomic"
"time"
Expand Down Expand Up @@ -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 {
Expand All @@ -55,22 +57,22 @@ func (a *Anvil) Start(ctx context.Context) error {
return errors.New("anvil already started")
}

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)
}

args = append(args, "--init", tempFile.Name())
}

anvilLog := a.log.New("role", "anvil", "chain.id", a.cfg.ChainId)
Expand All @@ -81,6 +83,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()
Expand All @@ -94,7 +100,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() {
Expand All @@ -110,8 +126,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 {
Expand All @@ -120,6 +134,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
}

Expand Down Expand Up @@ -182,12 +207,9 @@ func waitForAnvilClientToBeReady(ctx context.Context, client *rpc.Client, timeou
return fmt.Errorf("timed out waiting for response from client")
case <-ticker.C:
var result string
callErr := client.Call(&result, "web3_clientVersion")

if callErr != nil {
if err := client.Call(&result, "web3_clientVersion"); err != nil {
continue
}

if strings.HasPrefix(result, "anvil") {
return nil
}
Expand Down
32 changes: 32 additions & 0 deletions anvil/anvil_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
24 changes: 17 additions & 7 deletions op-simulator/op_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import (
"net/http/httputil"
"net/url"
"strconv"
"strings"
"sync/atomic"

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

const (
host = "127.0.0.1"
)

type Config struct {
Port uint64
}
Expand All @@ -30,10 +35,6 @@ type OpSimulator struct {
cfg *Config
}

const (
host = "127.0.0.1"
)

func New(log log.Logger, cfg *Config, anvil *anvil.Anvil) *OpSimulator {
return &OpSimulator{
log: log,
Expand All @@ -47,18 +48,27 @@ func (opSim *OpSimulator) Start(ctx context.Context) error {
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)))

hs, err := ophttp.StartHTTPServer(endpoint, mux)
hs, err := ophttp.StartHTTPServer(net.JoinHostPort(host, fmt.Sprintf("%d", opSim.cfg.Port)), mux)
if err != nil {
return fmt.Errorf("failed to start HTTP RPC server: %w", err)
}
opSim.log.Info(fmt.Sprintf("listening on %v", endpoint), "chain.id", opSim.ChainId())

opSim.log.Info("started op-simulator", "chain.id", opSim.ChainId(), "addr", hs.Addr())
opSim.httpServer = hs

if opSim.cfg.Port == 0 {
port, err := strconv.ParseInt(strings.Split(hs.Addr().String(), ":")[1], 10, 64)
if err != nil {
panic(fmt.Errorf("unexpected op-simulator listening port: %w", err))
}

opSim.cfg.Port = uint64(port)
}

return nil
}

Expand Down
12 changes: 6 additions & 6 deletions supersim.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ var genesisL2JSON []byte

var DefaultConfig = Config{
l1Chain: ChainConfig{
anvilConfig: anvil.Config{ChainId: 1, Port: 8545, Genesis: genesisL1JSON},
opSimConfig: opsim.Config{Port: 8546},
anvilConfig: anvil.Config{ChainId: 1, Port: 0, Genesis: genesisL1JSON},
opSimConfig: opsim.Config{Port: 0},
},
l2Chains: []ChainConfig{
{
anvilConfig: anvil.Config{ChainId: 10, Port: 9545, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 9546},
anvilConfig: anvil.Config{ChainId: 10, Port: 0, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 0},
},
{
anvilConfig: anvil.Config{ChainId: 30, Port: 9555, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 9556},
anvilConfig: anvil.Config{ChainId: 30, Port: 0, Genesis: genesisL2JSON},
opSimConfig: opsim.Config{Port: 0},
},
},
}
Expand Down
1 change: 0 additions & 1 deletion supersim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ type TestSuite struct {

func createTestSuite(t *testing.T) *TestSuite {
cfg := &DefaultConfig

testlog := testlog.Logger(t, log.LevelInfo)
supersim := NewSupersim(testlog, cfg)
t.Cleanup(func() {
Expand Down

0 comments on commit 76d432e

Please sign in to comment.