From 77402cc89be8635e5956571320f44bdf1c9caf30 Mon Sep 17 00:00:00 2001 From: corver Date: Mon, 4 Nov 2024 17:28:31 +0200 Subject: [PATCH] ci(e2e): add solver skeleton app --- .goreleaser-official.yaml | 18 ++- .goreleaser-snapshot.yaml | 15 +++ e2e/app/agent/prometheus.go | 7 +- e2e/app/agent/prometheus_internal_test.go | 2 +- .../testdata/TestPromGen_manifest1_gen.golden | 8 ++ .../TestPromGen_manifest1_update.golden | 8 ++ .../testdata/TestPromGen_manifest2_gen.golden | 8 ++ .../TestPromGen_manifest2_update.golden | 8 ++ .../testdata/TestPromGen_manifest3_gen.golden | 8 ++ .../TestPromGen_manifest3_update.golden | 8 ++ .../testdata/TestPromGen_manifest4_gen.golden | 8 ++ .../TestPromGen_manifest4_update.golden | 8 ++ e2e/app/definition_test.go | 2 +- e2e/app/setup.go | 50 +++++++- .../TestManifestServiceReference.golden | 2 + e2e/docker/compose.yaml.tmpl | 16 +++ e2e/docker/docker.go | 21 +-- .../TestComposeTemplate_commit.golden | 14 ++ ...estComposeTemplate_empheral_network.golden | 14 ++ ...seTemplate_empheral_network_upgrade.golden | 14 ++ e2e/vmcompose/provider.go | 3 + solver/Dockerfile | 15 +++ solver/app/app.go | 120 ++++++++++++++++++ solver/app/config.go | 64 ++++++++++ solver/app/config.toml.tpl | 51 ++++++++ solver/app/config_test.go | 32 +++++ .../app/testdata/default_solver.toml | 34 +++-- solver/cmd/cmd.go | 39 ++++++ solver/cmd/flags.go | 17 +++ solver/main.go | 11 ++ 30 files changed, 598 insertions(+), 27 deletions(-) create mode 100644 solver/Dockerfile create mode 100644 solver/app/app.go create mode 100644 solver/app/config.go create mode 100644 solver/app/config.toml.tpl create mode 100644 solver/app/config_test.go rename monitor/app/monitor.toml => solver/app/testdata/default_solver.toml (59%) create mode 100644 solver/cmd/cmd.go create mode 100644 solver/cmd/flags.go create mode 100644 solver/main.go diff --git a/.goreleaser-official.yaml b/.goreleaser-official.yaml index 1135b6ffa..bcb3d6b0e 100644 --- a/.goreleaser-official.yaml +++ b/.goreleaser-official.yaml @@ -9,7 +9,7 @@ project_name: omni # - Only linux/amd/arm docker images required # - Tag images with {Tag} and latest # -# relayer, monitor: +# relayer, monitor, solver: # - Internal facing (not external) # - Not released as binaries # - Only linux/amd docker image required. @@ -54,6 +54,15 @@ builds: ldflags: - -s -w -X github.com/omni-network/omni/lib/buildinfo.version={{.Tag}} + - id: solver + main: ./solver + binary: solver + env: [CGO_ENABLED=0] + goos: [linux] + goarch: [amd64] + ldflags: + - -s -w -X github.com/omni-network/omni/lib/buildinfo.version={{.Tag}} + dockers: - id: halo-amd64 ids: [halo] @@ -113,6 +122,13 @@ dockers: image_templates: - omniops/monitor:{{.Tag}} + - ids: [solver] + goos: linux + goarch: amd64 + dockerfile: ./solver/Dockerfile + image_templates: + - omniops/solver:{{.Tag}} + docker_manifests: - name_template: omniops/halo:{{.Tag}} image_templates: diff --git a/.goreleaser-snapshot.yaml b/.goreleaser-snapshot.yaml index 0a5aa4df8..77c6d12d5 100644 --- a/.goreleaser-snapshot.yaml +++ b/.goreleaser-snapshot.yaml @@ -27,6 +27,13 @@ builds: goos: [linux] goarch: [amd64] + - id: solver + main: ./solver + binary: solver + env: [CGO_ENABLED=0] + goos: [linux] + goarch: [amd64] + - id: omni main: ./cli/cmd/omni binary: omni @@ -74,6 +81,14 @@ dockers: - omniops/monitor:{{ .ShortCommit }} - omniops/monitor:main + - ids: [solver] + dockerfile: ./solver/Dockerfile + goos: linux + goarch: amd64 + image_templates: + - omniops/solver:{{ .ShortCommit }} + - omniops/solver:main + - ids: [anvilproxy] dockerfile: ./e2e/anvilproxy/Dockerfile goos: linux diff --git a/e2e/app/agent/prometheus.go b/e2e/app/agent/prometheus.go index a74b52d44..f962e0650 100644 --- a/e2e/app/agent/prometheus.go +++ b/e2e/app/agent/prometheus.go @@ -105,6 +105,11 @@ func genPromConfig(ctx context.Context, testnet types.Testnet, secrets Secrets, MetricsPath: "/metrics", targets: []string{fmt.Sprintf("monitor:%d", promPort)}, }, + { + JobName: "solver", + MetricsPath: "/metrics", + targets: []string{fmt.Sprintf("solver:%d", promPort)}, + }, }, } @@ -147,7 +152,7 @@ func (c promScrapConfig) Targets() string { // It replaces the geth targets with provided. // It replaces the host label. func ConfigForHost(bz []byte, newHost string, halos []string, geths []string, services map[string]bool) []byte { - for _, service := range []string{"relayer", "monitor"} { + for _, service := range []string{"relayer", "monitor", "solver"} { if services[service] { continue } diff --git a/e2e/app/agent/prometheus_internal_test.go b/e2e/app/agent/prometheus_internal_test.go index e42651683..d04b2976d 100644 --- a/e2e/app/agent/prometheus_internal_test.go +++ b/e2e/app/agent/prometheus_internal_test.go @@ -65,7 +65,7 @@ func TestPromGen(t *testing.T) { nodes: []string{"validator01", "validator02", "fullnode03"}, hostname: "vm", newNodes: []string{"fullnode04"}, - newServices: []string{"relayer", "monitor"}, + newServices: []string{"relayer", "monitor", "solver"}, agentSecrets: true, }, } diff --git a/e2e/app/agent/testdata/TestPromGen_manifest1_gen.golden b/e2e/app/agent/testdata/TestPromGen_manifest1_gen.golden index 88739b325..1d323d19f 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest1_gen.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest1_gen.golden @@ -35,3 +35,11 @@ scrape_configs: network: 'manifest1-localhost' host: 'localhost' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [solver:26660] # solver targets + labels: + network: 'manifest1-localhost' + host: 'localhost' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest1_update.golden b/e2e/app/agent/testdata/TestPromGen_manifest1_update.golden index 82ee1a84b..ebaf096f8 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest1_update.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest1_update.golden @@ -35,3 +35,11 @@ scrape_configs: network: 'manifest1-localhost' host: 'localhost-2' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [] # solver targets + labels: + network: 'manifest1-localhost' + host: 'localhost-2' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest2_gen.golden b/e2e/app/agent/testdata/TestPromGen_manifest2_gen.golden index 252b07c7a..b29b49a20 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest2_gen.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest2_gen.golden @@ -47,3 +47,11 @@ scrape_configs: network: 'staging' host: 'vm' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [solver:26660] # solver targets + labels: + network: 'staging' + host: 'vm' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest2_update.golden b/e2e/app/agent/testdata/TestPromGen_manifest2_update.golden index 8bce48a22..5b4ef3fa5 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest2_update.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest2_update.golden @@ -47,3 +47,11 @@ scrape_configs: network: 'staging' host: 'vm-2' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [] # solver targets + labels: + network: 'staging' + host: 'vm-2' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest3_gen.golden b/e2e/app/agent/testdata/TestPromGen_manifest3_gen.golden index e84a2a3e9..4aba20181 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest3_gen.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest3_gen.golden @@ -35,3 +35,11 @@ scrape_configs: network: 'manifest3-localhost' host: 'localhost' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [solver:26660] # solver targets + labels: + network: 'manifest3-localhost' + host: 'localhost' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest3_update.golden b/e2e/app/agent/testdata/TestPromGen_manifest3_update.golden index a3efaead4..e778e59fb 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest3_update.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest3_update.golden @@ -35,3 +35,11 @@ scrape_configs: network: 'manifest3-localhost' host: 'localhost-2' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [] # solver targets + labels: + network: 'manifest3-localhost' + host: 'localhost-2' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest4_gen.golden b/e2e/app/agent/testdata/TestPromGen_manifest4_gen.golden index f1dba0239..69a365874 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest4_gen.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest4_gen.golden @@ -47,3 +47,11 @@ scrape_configs: network: 'staging' host: 'vm' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [solver:26660] # solver targets + labels: + network: 'staging' + host: 'vm' + diff --git a/e2e/app/agent/testdata/TestPromGen_manifest4_update.golden b/e2e/app/agent/testdata/TestPromGen_manifest4_update.golden index ff0c44912..1e95b18a0 100644 --- a/e2e/app/agent/testdata/TestPromGen_manifest4_update.golden +++ b/e2e/app/agent/testdata/TestPromGen_manifest4_update.golden @@ -47,3 +47,11 @@ scrape_configs: network: 'staging' host: 'vm-2' + - job_name: "solver" + metrics_path: "/metrics" + static_configs: + - targets: [solver:26660] # solver targets + labels: + network: 'staging' + host: 'vm-2' + diff --git a/e2e/app/definition_test.go b/e2e/app/definition_test.go index d8c47fa0e..ec5cda648 100644 --- a/e2e/app/definition_test.go +++ b/e2e/app/definition_test.go @@ -29,7 +29,7 @@ func TestManifestServiceReference(t *testing.T) { require.NoError(t, err) services := keys(infraData.Instances) - services = append(services, "relayer", "monitor") + services = append(services, "relayer", "monitor", "solver") sort.Strings(services) ref[manifest.Network] = services } diff --git a/e2e/app/setup.go b/e2e/app/setup.go index b071eca16..f99066546 100644 --- a/e2e/app/setup.go +++ b/e2e/app/setup.go @@ -30,6 +30,7 @@ import ( "github.com/omni-network/omni/lib/xchain" monapp "github.com/omni-network/omni/monitor/app" relayapp "github.com/omni-network/omni/relayer/app" + solverapp "github.com/omni-network/omni/solver/app" "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/crypto" @@ -54,6 +55,8 @@ const ( ) // Setup sets up the testnet configuration. +// +//nolint:gocyclo // Just multiple sequential steps. func Setup(ctx context.Context, def Definition, depCfg DeployConfig) error { log.Info(ctx, "Setup testnet", "dir", def.Testnet.Dir) @@ -114,6 +117,10 @@ func Setup(ctx context.Context, def Definition, depCfg DeployConfig) error { return err } + if err := writeSolverConfig(ctx, def, logCfg); err != nil { + return err + } + if err := writeAnvilState(def.Testnet); err != nil { return err } @@ -492,6 +499,45 @@ func writeRelayerConfig(ctx context.Context, def Definition, logCfg log.Config) return nil } +func writeSolverConfig(_ context.Context, def Definition, logCfg log.Config) error { + confRoot := filepath.Join(def.Testnet.Dir, "solver") + + const ( + privKeyFile = "privatekey" + configFile = "solver.toml" + ) + + if err := os.MkdirAll(confRoot, 0o755); err != nil { + return errors.Wrap(err, "mkdir", "path", confRoot) + } + + // Save network config + endpoints := internalEndpoints(def, "") + if def.Infra.GetInfrastructureData().Provider == vmcompose.ProviderName { + endpoints = ExternalEndpoints(def) + } + + // TODO(corver): save proper private key + privKey, err := ethcrypto.GenerateKey() + if err != nil { + return errors.Wrap(err, "generate private key") + } + if err := ethcrypto.SaveECDSA(filepath.Join(confRoot, privKeyFile), privKey); err != nil { + return errors.Wrap(err, "write private key") + } + + solverCfg := solverapp.DefaultConfig() + solverCfg.PrivateKey = privKeyFile + solverCfg.Network = def.Testnet.Network + solverCfg.RPCEndpoints = endpoints + + if err := solverapp.WriteConfigTOML(solverCfg, logCfg, filepath.Join(confRoot, configFile)); err != nil { + return errors.Wrap(err, "write solver config") + } + + return nil +} + func writeMonitorConfig(ctx context.Context, def Definition, logCfg log.Config, valPrivKeys []crypto.PrivKey) error { confRoot := filepath.Join(def.Testnet.Dir, "monitor") @@ -530,7 +576,7 @@ func writeMonitorConfig(ctx context.Context, def Definition, logCfg log.Config, // Save private key privKey, err := eoa.PrivateKey(ctx, def.Testnet.Network, eoa.RoleMonitor) if err != nil { - return errors.Wrap(err, "get relayer key") + return errors.Wrap(err, "get monitor key") } if err := ethcrypto.SaveECDSA(filepath.Join(confRoot, privKeyFile), privKey); err != nil { return errors.Wrap(err, "write private key") @@ -568,7 +614,7 @@ func writeMonitorConfig(ctx context.Context, def Definition, logCfg log.Config, cfg.XFeeMngr.CoinGeckoAPIKey = def.Cfg.CoinGeckoAPIKey if err := monapp.WriteConfigTOML(cfg, logCfg, filepath.Join(confRoot, configFile)); err != nil { - return errors.Wrap(err, "write relayer config") + return errors.Wrap(err, "write monitor config") } return nil diff --git a/e2e/app/testdata/TestManifestServiceReference.golden b/e2e/app/testdata/TestManifestServiceReference.golden index 2e32ed289..fa5e2b013 100644 --- a/e2e/app/testdata/TestManifestServiceReference.golden +++ b/e2e/app/testdata/TestManifestServiceReference.golden @@ -30,6 +30,7 @@ "seed01_evm", "seed02", "seed02_evm", + "solver", "validator01", "validator01_evm", "validator02", @@ -46,6 +47,7 @@ "relayer", "seed01", "seed01_evm", + "solver", "validator01", "validator01_evm", "validator02", diff --git a/e2e/docker/compose.yaml.tmpl b/e2e/docker/compose.yaml.tmpl index fcbd32f36..f3101a9bb 100644 --- a/e2e/docker/compose.yaml.tmpl +++ b/e2e/docker/compose.yaml.tmpl @@ -127,6 +127,22 @@ services: {{ if $.Network }}ipv4_address: 10.186.73.201{{ end }} {{ end }} +{{- if .Solver }} + solver: + labels: + e2e: true + container_name: solver + image: omniops/solver:{{or .SolverTag "main"}} + restart: unless-stopped + ports: + - 26660 # Prometheus and pprof + volumes: + - ./solver:/solver + networks: + {{ $.NetworkName }}: + {{ if $.Network }}ipv4_address: 10.186.73.203{{ end }} +{{ end }} + {{- if .Prometheus }} prometheus: labels: diff --git a/e2e/docker/docker.go b/e2e/docker/docker.go index c747b1166..cde6bb3ba 100644 --- a/e2e/docker/docker.go +++ b/e2e/docker/docker.go @@ -89,6 +89,7 @@ func (p *Provider) Setup() error { Relayer: true, Prometheus: p.testnet.Prometheus, Monitor: true, + Solver: true, GethVerbosity: 3, // Info } def = SetImageTags(def, p.testnet.Manifest, p.omniTag) @@ -180,12 +181,15 @@ type ComposeDef struct { OmniEVMs []types.OmniEVM Anvils []types.AnvilChain - Monitor bool - AnvilProxyTag string - RelayerTag string + Monitor bool + Relayer bool + Solver bool + Prometheus bool + MonitorTag string - Relayer bool - Prometheus bool + RelayerTag string + SolverTag string + AnvilProxyTag string } func (ComposeDef) GethTag() string { @@ -211,6 +215,7 @@ func (c ComposeDef) NodeOmniEVMs() map[string]string { func SetImageTags(def ComposeDef, manifest types.Manifest, omniImgTag string) ComposeDef { anvilProxyTag := omniImgTag + // TODO(corver): Remove pinned tags since they are not used. monitorTag := omniImgTag if manifest.PinnedMonitorTag != "" { monitorTag = manifest.PinnedMonitorTag @@ -224,6 +229,7 @@ func SetImageTags(def ComposeDef, manifest types.Manifest, omniImgTag string) Co def.AnvilProxyTag = anvilProxyTag def.MonitorTag = monitorTag def.RelayerTag = relayerTag + def.SolverTag = omniImgTag return def } @@ -300,15 +306,14 @@ func additionalServices(testnet types.Testnet) []string { resp = append(resp, "prometheus") } - resp = append(resp, "monitor") - for _, omniEVM := range testnet.OmniEVMs { resp = append(resp, omniEVM.InstanceName) } for _, anvil := range testnet.AnvilChains { resp = append(resp, anvil.Chain.Name) } - resp = append(resp, "relayer") + + resp = append(resp, "monitor", "relayer", "solver") return resp } diff --git a/e2e/docker/testdata/TestComposeTemplate_commit.golden b/e2e/docker/testdata/TestComposeTemplate_commit.golden index f987efc96..976ea6cf7 100644 --- a/e2e/docker/testdata/TestComposeTemplate_commit.golden +++ b/e2e/docker/testdata/TestComposeTemplate_commit.golden @@ -129,6 +129,20 @@ services: test: ipv4_address: 10.186.73.201 + solver: + labels: + e2e: true + container_name: solver + image: omniops/solver:7d1ae53 + restart: unless-stopped + ports: + - 26660 # Prometheus and pprof + volumes: + - ./solver:/solver + networks: + test: + ipv4_address: 10.186.73.203 + prometheus: labels: e2e: true diff --git a/e2e/docker/testdata/TestComposeTemplate_empheral_network.golden b/e2e/docker/testdata/TestComposeTemplate_empheral_network.golden index 4812aaabf..314a53495 100644 --- a/e2e/docker/testdata/TestComposeTemplate_empheral_network.golden +++ b/e2e/docker/testdata/TestComposeTemplate_empheral_network.golden @@ -129,6 +129,20 @@ services: test: ipv4_address: 10.186.73.201 + solver: + labels: + e2e: true + container_name: solver + image: omniops/solver:main + restart: unless-stopped + ports: + - 26660 # Prometheus and pprof + volumes: + - ./solver:/solver + networks: + test: + ipv4_address: 10.186.73.203 + prometheus: labels: e2e: true diff --git a/e2e/docker/testdata/TestComposeTemplate_empheral_network_upgrade.golden b/e2e/docker/testdata/TestComposeTemplate_empheral_network_upgrade.golden index b3a3fc7f9..fc293062a 100644 --- a/e2e/docker/testdata/TestComposeTemplate_empheral_network_upgrade.golden +++ b/e2e/docker/testdata/TestComposeTemplate_empheral_network_upgrade.golden @@ -129,6 +129,20 @@ services: test: ipv4_address: 10.186.73.201 + solver: + labels: + e2e: true + container_name: solver + image: omniops/solver:main + restart: unless-stopped + ports: + - 26660 # Prometheus and pprof + volumes: + - ./solver:/solver + networks: + test: + ipv4_address: 10.186.73.203 + prometheus: labels: e2e: true diff --git a/e2e/vmcompose/provider.go b/e2e/vmcompose/provider.go index db0eaccc6..66ba35eec 100644 --- a/e2e/vmcompose/provider.go +++ b/e2e/vmcompose/provider.go @@ -96,6 +96,7 @@ func (p *Provider) Setup() error { Anvils: anvilChains, Relayer: services["relayer"], Monitor: services["monitor"], + Solver: services["solver"], Prometheus: p.Testnet.Prometheus, GethVerbosity: gethVerbosity, } @@ -194,6 +195,8 @@ func (p *Provider) Upgrade(ctx context.Context, cfg types.ServiceConfig) error { addFile("relayer", "privatekey") addFile("monitor", "monitor.toml") addFile("monitor", "privatekey") + addFile("solver", "solver.toml") + addFile("solver", "privatekey") addFile("prometheus", "prometheus.yaml") // Prometheus isn't a "service", so not actually copied diff --git a/solver/Dockerfile b/solver/Dockerfile new file mode 100644 index 000000000..e23c9dba2 --- /dev/null +++ b/solver/Dockerfile @@ -0,0 +1,15 @@ +FROM scratch + +# Install ca-certificates (for https to rollups) +COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +# Copy solver binary and rename to /app +COPY solver /app + +# Mount config directory at /solver +VOLUME ["/solver"] + +# Set working directory to /solver, so it automatically reads config from here. +WORKDIR /solver + +ENTRYPOINT ["/app"] diff --git a/solver/app/app.go b/solver/app/app.go new file mode 100644 index 000000000..eb1793f28 --- /dev/null +++ b/solver/app/app.go @@ -0,0 +1,120 @@ +package solver + +import ( + "context" + "net/http" + "net/http/pprof" + "time" + + "github.com/omni-network/omni/contracts/bindings" + "github.com/omni-network/omni/halo/genutil/evm/predeploys" + "github.com/omni-network/omni/lib/buildinfo" + "github.com/omni-network/omni/lib/errors" + "github.com/omni-network/omni/lib/ethclient" + "github.com/omni-network/omni/lib/log" + "github.com/omni-network/omni/lib/netconf" + "github.com/omni-network/omni/lib/xchain" + + "github.com/ethereum/go-ethereum/common" + + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +// Run starts the solver service. +func Run(ctx context.Context, cfg Config) error { + log.Info(ctx, "Starting solver service") + + buildinfo.Instrument(ctx) + + // Start monitoring first, so app is "up" + monitorChan := serveMonitoring(cfg.MonitoringAddr) + + portalReg, err := makePortalRegistry(cfg.Network, cfg.RPCEndpoints) + if err != nil { + return err + } + + network, err := netconf.AwaitOnExecutionChain(ctx, cfg.Network, portalReg, cfg.RPCEndpoints.Keys()) + if err != nil { + return err + } + + _, err = initializeEthClients(network.EVMChains(), cfg.RPCEndpoints) + if err != nil { + return err + } + + select { + case <-ctx.Done(): + log.Info(ctx, "Shutdown detected, stopping...") + return nil + case err := <-monitorChan: + return err + } +} + +// serveMonitoring starts a goroutine that serves the monitoring API. It +// returns a channel that will receive an error if the server fails to start. +func serveMonitoring(address string) <-chan error { + errChan := make(chan error) + go func() { + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.Handler()) + + // Copied from net/http/pprof/pprof.go + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + + srv := &http.Server{ + Addr: address, + ReadHeaderTimeout: 5 * time.Second, + IdleTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + Handler: mux, + } + errChan <- errors.Wrap(srv.ListenAndServe(), "serve monitoring") + }() + + return errChan +} + +func makePortalRegistry(network netconf.ID, endpoints xchain.RPCEndpoints) (*bindings.PortalRegistry, error) { + meta := netconf.MetadataByID(network, network.Static().OmniExecutionChainID) + rpc, err := endpoints.ByNameOrID(meta.Name, meta.ChainID) + if err != nil { + return nil, err + } + + ethCl, err := ethclient.Dial(meta.Name, rpc) + if err != nil { + return nil, err + } + + resp, err := bindings.NewPortalRegistry(common.HexToAddress(predeploys.PortalRegistry), ethCl) + if err != nil { + return nil, errors.Wrap(err, "create portal registry") + } + + return resp, nil +} + +// initializeEthClients initializes the RPC clients for the given chains. +func initializeEthClients(chains []netconf.Chain, endpoints xchain.RPCEndpoints) (map[uint64]ethclient.Client, error) { + rpcClientPerChain := make(map[uint64]ethclient.Client) + for _, chain := range chains { + rpc, err := endpoints.ByNameOrID(chain.Name, chain.ID) + if err != nil { + return nil, err + } + c, err := ethclient.Dial(chain.Name, rpc) + if err != nil { + return nil, errors.Wrap(err, "dial rpc", "chain_name", chain.Name, "chain_id", chain.ID, "rpc_url", rpc) + } + rpcClientPerChain[chain.ID] = c + } + + return rpcClientPerChain, nil +} diff --git a/solver/app/config.go b/solver/app/config.go new file mode 100644 index 000000000..ecb5f5faa --- /dev/null +++ b/solver/app/config.go @@ -0,0 +1,64 @@ +package solver + +import ( + "bytes" + "text/template" + + "github.com/omni-network/omni/lib/buildinfo" + "github.com/omni-network/omni/lib/errors" + "github.com/omni-network/omni/lib/log" + "github.com/omni-network/omni/lib/netconf" + "github.com/omni-network/omni/lib/xchain" + + cmtos "github.com/cometbft/cometbft/libs/os" + + _ "embed" +) + +type Config struct { + RPCEndpoints xchain.RPCEndpoints + Network netconf.ID + MonitoringAddr string + PrivateKey string + DBDir string +} + +func DefaultConfig() Config { + return Config{ + PrivateKey: "solver.key", + MonitoringAddr: ":26660", + DBDir: "./db", + } +} + +//go:embed config.toml.tpl +var tomlTemplate []byte + +// WriteConfigTOML writes the toml solver config to disk. +func WriteConfigTOML(cfg Config, logCfg log.Config, path string) error { + t, err := template.New("").Parse(string(tomlTemplate)) + if err != nil { + return errors.Wrap(err, "parse template") + } + + s := struct { + Config + Log log.Config + Version string + }{ + Config: cfg, + Log: logCfg, + Version: buildinfo.Version(), + } + + var buffer bytes.Buffer + if err := t.Execute(&buffer, s); err != nil { + return errors.Wrap(err, "execute template") + } + + if err := cmtos.WriteFile(path, buffer.Bytes(), 0o644); err != nil { + return errors.Wrap(err, "write config") + } + + return nil +} diff --git a/solver/app/config.toml.tpl b/solver/app/config.toml.tpl new file mode 100644 index 000000000..d5252f975 --- /dev/null +++ b/solver/app/config.toml.tpl @@ -0,0 +1,51 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +# The version of the Halo binary that created or +# last modified the config file. Do not modify this. +version = "{{ .Version}}" + +# Omni network to participate in: mainnet, testnet, or devnet. +network = "{{ .Network }}" + +####################################################################### +### Solver Options ### +####################################################################### + +# Path to the ethereum private key used to sign avs omni sync transactions. +private-key = "{{ .PrivateKey }}" + +# The address that the solver listens for metric scrape requests. +monitoring-addr = "{{ .MonitoringAddr }}" + +####################################################################### +### X-Chain ### +####################################################################### + +[xchain] + +# Cross-chain EVM RPC endpoints to use for voting; only required for validators. One per supported EVM is required. +# It is strongly advised to operate fullnodes for each chain and NOT to use free public RPCs. +[xchain.evm-rpc-endpoints] +{{- if not .RPCEndpoints }} +# ethereum = "http://my-ethreum-node:8545" +# optimism = "https://my-op-node.com" +{{ end -}} +{{- range $key, $value := .RPCEndpoints }} +{{ $key }} = "{{ $value }}" +{{ end }} + +####################################################################### +### Logging Options ### +####################################################################### + +[log] +# Logging level. Note cometBFT internal logs are configured in config.yaml. +# Options are: debug, info, warn, error. +level = "{{ .Log.Level }}" + +# Logging format. Options are: console, json. +format = "{{ .Log.Format }}" + +# Logging color if console format is chosen. Options are: auto, force, disable. +color = "{{ .Log.Color }}" diff --git a/solver/app/config_test.go b/solver/app/config_test.go new file mode 100644 index 000000000..a75380716 --- /dev/null +++ b/solver/app/config_test.go @@ -0,0 +1,32 @@ +package solver_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/omni-network/omni/lib/log" + "github.com/omni-network/omni/lib/tutil" + solver "github.com/omni-network/omni/solver/app" + + "github.com/stretchr/testify/require" +) + +//go:generate go test . -golden -clean + +func TestDefaultConfigReference(t *testing.T) { + t.Parallel() + tempDir := t.TempDir() + + cfg := solver.DefaultConfig() + + path := filepath.Join(tempDir, "solver.toml") + + require.NoError(t, os.MkdirAll(tempDir, 0o755)) + require.NoError(t, solver.WriteConfigTOML(cfg, log.DefaultConfig(), path)) + + b, err := os.ReadFile(path) + require.NoError(t, err) + + tutil.RequireGoldenBytes(t, b, tutil.WithFilename("default_solver.toml")) +} diff --git a/monitor/app/monitor.toml b/solver/app/testdata/default_solver.toml similarity index 59% rename from monitor/app/monitor.toml rename to solver/app/testdata/default_solver.toml index 08d645510..18632406c 100644 --- a/monitor/app/monitor.toml +++ b/solver/app/testdata/default_solver.toml @@ -3,18 +3,34 @@ # The version of the Halo binary that created or # last modified the config file. Do not modify this. -version = "v0.1.1" +version = "main" + +# Omni network to participate in: mainnet, testnet, or devnet. +network = "" ####################################################################### -### Monitor Options ### +### Solver Options ### ####################################################################### -# The path to the Omni network configuration file. -network-file = "network.json" +# Path to the ethereum private key used to sign avs omni sync transactions. +private-key = "solver.key" -# The address that the monitor listens for metric scrape requests. +# The address that the solver listens for metric scrape requests. monitoring-addr = ":26660" +####################################################################### +### X-Chain ### +####################################################################### + +[xchain] + +# Cross-chain EVM RPC endpoints to use for voting; only required for validators. One per supported EVM is required. +# It is strongly advised to operate fullnodes for each chain and NOT to use free public RPCs. +[xchain.evm-rpc-endpoints] +# ethereum = "http://my-ethreum-node:8545" +# optimism = "https://my-op-node.com" + + ####################################################################### ### Logging Options ### ####################################################################### @@ -29,11 +45,3 @@ format = "console" # Logging color if console format is chosen. Options are: auto, force, disable. color = "auto" - -####################################################################### -### Load Generation ### -####################################################################### - -# Note that load generation is only used for testing purposes; ie on devent or staging. -[loadgen] -validator-keys = ["path/1", "path/2"] diff --git a/solver/cmd/cmd.go b/solver/cmd/cmd.go new file mode 100644 index 000000000..fcef81543 --- /dev/null +++ b/solver/cmd/cmd.go @@ -0,0 +1,39 @@ +// Package cmd provides the cli for running the solver service +package cmd + +import ( + libcmd "github.com/omni-network/omni/lib/cmd" + "github.com/omni-network/omni/lib/log" + solver "github.com/omni-network/omni/solver/app" + + "github.com/spf13/cobra" +) + +// New returns a new root cobra command that handles our command line tool. +func New() *cobra.Command { + cmd := libcmd.NewRootCmd( + "solver", + "Service that solves Omni network intents", + ) + + cfg := solver.DefaultConfig() + bindRunFlags(cmd.Flags(), &cfg) + + logCfg := log.DefaultConfig() + log.BindFlags(cmd.Flags(), &logCfg) + + cmd.RunE = func(cmd *cobra.Command, _ []string) error { + ctx, err := log.Init(cmd.Context(), logCfg) + if err != nil { + return err + } + + if err := libcmd.LogFlags(ctx, cmd.Flags()); err != nil { + return err + } + + return solver.Run(ctx, cfg) + } + + return cmd +} diff --git a/solver/cmd/flags.go b/solver/cmd/flags.go new file mode 100644 index 000000000..e710806bf --- /dev/null +++ b/solver/cmd/flags.go @@ -0,0 +1,17 @@ +package cmd + +import ( + "github.com/omni-network/omni/lib/netconf" + "github.com/omni-network/omni/lib/xchain" + solver "github.com/omni-network/omni/solver/app" + + "github.com/spf13/pflag" +) + +func bindRunFlags(flags *pflag.FlagSet, cfg *solver.Config) { + netconf.BindFlag(flags, &cfg.Network) + xchain.BindFlags(flags, &cfg.RPCEndpoints) + flags.StringVar(&cfg.PrivateKey, "private-key", cfg.PrivateKey, "The path to the private key e.g path/private.key") + flags.StringVar(&cfg.MonitoringAddr, "monitoring-addr", cfg.MonitoringAddr, "The address to bind the monitoring server") + flags.StringVar(&cfg.DBDir, "db-dir", cfg.DBDir, "The path to the database directory") +} diff --git a/solver/main.go b/solver/main.go new file mode 100644 index 000000000..6929cd641 --- /dev/null +++ b/solver/main.go @@ -0,0 +1,11 @@ +// Command solver is the main entry point for the solver service. +package main + +import ( + libcmd "github.com/omni-network/omni/lib/cmd" + solvercmd "github.com/omni-network/omni/solver/cmd" +) + +func main() { + libcmd.Main(solvercmd.New()) +}