docker compose

This commit is contained in:
a 2022-10-25 05:20:17 -05:00
parent 6b794b24db
commit 454fc0a576
59 changed files with 136 additions and 328 deletions

View File

@ -28,14 +28,12 @@ RUN apk add alpine-sdk
WORKDIR /wd
COPY ["go.mod","go.sum","/wd/"]
COPY cmd cmd
RUN go build -o rpcdaemon ./cmd/rpcdaemon
RUN go build -o server ./cmd/server
RUN go build -o otter ./cmd/otter
FROM alpine:3.15.0
RUN apk add alpine-sdk
WORKDIR /wd
COPY --from=gobuilder /wd/rpcdaemon /usr/bin/rpcdaemon
COPY --from=gobuilder /wd/server /usr/bin/server
COPY --from=gobuilder /wd/otter /usr/bin/otter
COPY --from=builder /otterscan-build/dist /wd/dist
COPY --from=logobuilder /assets /wd/dist

View File

@ -2,17 +2,15 @@ package cli
import (
"context"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/ledgerwatch/erigon-lib/common/dir"
libstate "github.com/ledgerwatch/erigon-lib/state"
"github.com/ledgerwatch/erigon/eth/ethconfig"
@ -20,9 +18,9 @@ import (
"github.com/ledgerwatch/erigon/turbo/debug"
"github.com/ledgerwatch/erigon/turbo/logging"
"github.com/wmitsuda/otterscan/cmd/rpcdaemon/cli/httpcfg"
"github.com/wmitsuda/otterscan/cmd/rpcdaemon/health"
"github.com/wmitsuda/otterscan/cmd/rpcdaemon/rpcservices"
"github.com/wmitsuda/otterscan/cmd/otter/cli/httpcfg"
"github.com/wmitsuda/otterscan/cmd/otter/health"
"github.com/wmitsuda/otterscan/cmd/otter/rpcservices"
"github.com/ledgerwatch/erigon-lib/direct"
"github.com/ledgerwatch/erigon-lib/gointerfaces"
@ -35,8 +33,6 @@ import (
"github.com/ledgerwatch/erigon-lib/kv/remotedb"
"github.com/ledgerwatch/erigon-lib/kv/remotedbserver"
"github.com/ledgerwatch/erigon/cmd/utils"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/common/paths"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/node"
@ -57,14 +53,17 @@ import (
)
var rootCmd = &cobra.Command{
Use: "rpcdaemon",
Short: "rpcdaemon is JSON RPC server that connects to Erigon node for remote DB access",
Use: "otter",
Short: "otterscan is a chain explorer that can also host a custom JSON RPC server that connects to an Erigon node",
}
func RootCommand() (*cobra.Command, *httpcfg.HttpCfg) {
utils.CobraFlags(rootCmd, debug.Flags, utils.MetricFlags, logging.Flags)
cfg := &httpcfg.HttpCfg{Enabled: true, StateCache: kvcache.DefaultCoherentConfig}
cfg := &httpcfg.HttpCfg{}
cfg.Enabled = true
cfg.StateCache = kvcache.DefaultCoherentConfig
rootCmd.PersistentFlags().StringVar(&cfg.PrivateApiAddr, "private.api.addr", "127.0.0.1:9090", "private api network address, for example: 127.0.0.1:9090")
rootCmd.PersistentFlags().StringVar(&cfg.DataDir, "datadir", "", "path to Erigon working directory")
rootCmd.PersistentFlags().StringVar(&cfg.HttpListenAddress, "http.addr", nodecfg.DefaultHTTPHost, "HTTP-RPC server listening interface")
@ -105,6 +104,16 @@ func RootCommand() (*cobra.Command, *httpcfg.HttpCfg) {
panic(err)
}
// otterscan server setting
rootCmd.PersistentFlags().BoolVar(&cfg.OtsServerDisable, "ots.server.addr", false, "disable ots server to run rpc daemon only")
rootCmd.PersistentFlags().StringVar(&cfg.OtsStaticDir, "ots.static.dir", "./dist", "dir to serve static files from")
rootCmd.PersistentFlags().BoolVar(&cfg.DisableRpcDaemon, "disable.rpc.daemon", false, "dont run rpc daemon, for use when specifying external rtpc daemon")
rootCmd.PersistentFlags().StringVar(&cfg.OtsBeaconApiUrl, "ots.beaconapi.url", "http://localhost:3500", "where the website will make request for beacon api")
rootCmd.PersistentFlags().StringVar(&cfg.OtsRpcDaemonUrl, "ots.rpcdaemon.url", "/rpc", "where the website will make request for beacon api")
rootCmd.PersistentFlags().StringVar(&cfg.OtsExternalAssetUrl, "ots.externalasset.url", "/", "where website will make request for assets")
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if err := debug.SetupCobra(cmd); err != nil {
return err
@ -442,23 +451,14 @@ func RemoteServices(ctx context.Context, cfg httpcfg.HttpCfg, logger log.Logger,
return db, borDb, eth, txPool, mining, stateCache, blockReader, ff, agg, err
}
func StartRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rpc.API, authAPI []rpc.API) error {
if len(authAPI) > 0 {
engineInfo, err := startAuthenticatedRpcServer(cfg, authAPI)
if err != nil {
return err
}
go stopAuthenticatedRpcServer(ctx, engineInfo)
}
func StartRpcServer(ctx context.Context, r chi.Router, cfg httpcfg.HttpCfg, rpcAPI []rpc.API) error {
if cfg.Enabled {
return startRegularRpcServer(ctx, cfg, rpcAPI)
return startServer(ctx, r, cfg, rpcAPI)
}
return nil
}
func startRegularRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rpc.API) error {
func startServer(ctx context.Context, r chi.Router, cfg httpcfg.HttpCfg, rpcAPI []rpc.API) error {
// register apis and create handler stack
httpEndpoint := fmt.Sprintf("%s:%d", cfg.HttpListenAddress, cfg.HttpPort)
@ -500,126 +500,60 @@ func startRegularRpcServer(ctx context.Context, cfg httpcfg.HttpCfg, rpcAPI []rp
if err != nil {
return err
}
listener, _, err := node.StartHTTPEndpoint(httpEndpoint, cfg.HTTPTimeouts, apiHandler)
r.Mount("/rpc", apiHandler)
listener, _, err := node.StartHTTPEndpoint(httpEndpoint, cfg.HTTPTimeouts, r)
if err != nil {
return fmt.Errorf("could not start RPC api: %w", err)
}
info := []interface{}{"url", httpEndpoint, "ws", cfg.WebsocketEnabled,
info := &[]interface{}{"url", httpEndpoint, "ws", cfg.WebsocketEnabled,
"ws.compression", cfg.WebsocketCompression, "grpc", cfg.GRPCServerEnabled}
var (
healthServer *grpcHealth.Server
grpcServer *grpc.Server
grpcListener net.Listener
grpcEndpoint string
)
if cfg.GRPCServerEnabled {
grpcEndpoint = fmt.Sprintf("%s:%d", cfg.GRPCListenAddress, cfg.GRPCPort)
if grpcListener, err = net.Listen("tcp", grpcEndpoint); err != nil {
return fmt.Errorf("could not start GRPC listener: %w", err)
startGrpcServer(ctx, info, cfg)
if err != nil {
return fmt.Errorf("could not start GRPC api: %w", err)
}
grpcServer = grpc.NewServer()
if cfg.GRPCHealthCheckEnabled {
healthServer = grpcHealth.NewServer()
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
}
go grpcServer.Serve(grpcListener)
info = append(info, "grpc.port", cfg.GRPCPort)
}
log.Info("HTTP endpoint opened", info...)
log.Info("HTTP endpoint opened", *info...)
defer func() {
srv.Stop()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = listener.Shutdown(shutdownCtx)
log.Info("HTTP endpoint closed", "url", httpEndpoint)
if cfg.GRPCServerEnabled {
if cfg.GRPCHealthCheckEnabled {
healthServer.Shutdown()
}
grpcServer.GracefulStop()
_ = grpcListener.Close()
log.Info("GRPC endpoint closed", "url", grpcEndpoint)
}
}()
<-ctx.Done()
log.Info("Exiting...")
return nil
}
type engineInfo struct {
Srv *rpc.Server
EngineSrv *rpc.Server
EngineListener *http.Server
EngineHttpEndpoint string
}
func startAuthenticatedRpcServer(cfg httpcfg.HttpCfg, rpcAPI []rpc.API) (*engineInfo, error) {
log.Trace("TraceRequests = %t\n", cfg.TraceRequests)
srv := rpc.NewServer(cfg.RpcBatchConcurrency, cfg.TraceRequests, cfg.RpcStreamingDisable)
engineListener, engineSrv, engineHttpEndpoint, err := createEngineListener(cfg, rpcAPI)
if err != nil {
return nil, fmt.Errorf("could not start RPC api for engine: %w", err)
func startGrpcServer(ctx context.Context, info *[]any, cfg httpcfg.HttpCfg) (err error) {
var (
healthServer *grpcHealth.Server
grpcServer *grpc.Server
grpcListener net.Listener
grpcEndpoint string
)
grpcEndpoint = fmt.Sprintf("%s:%d", cfg.GRPCListenAddress, cfg.GRPCPort)
if grpcListener, err = net.Listen("tcp", grpcEndpoint); err != nil {
return fmt.Errorf("could not start GRPC listener: %w", err)
}
return &engineInfo{Srv: srv, EngineSrv: engineSrv, EngineListener: engineListener, EngineHttpEndpoint: engineHttpEndpoint}, nil
}
grpcServer = grpc.NewServer()
if cfg.GRPCHealthCheckEnabled {
healthServer = grpcHealth.NewServer()
grpc_health_v1.RegisterHealthServer(grpcServer, healthServer)
}
go grpcServer.Serve(grpcListener)
*info = append(*info, "grpc.port", cfg.GRPCPort)
func stopAuthenticatedRpcServer(ctx context.Context, engineInfo *engineInfo) {
defer func() {
engineInfo.Srv.Stop()
if engineInfo.EngineSrv != nil {
engineInfo.EngineSrv.Stop()
}
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if engineInfo.EngineListener != nil {
_ = engineInfo.EngineListener.Shutdown(shutdownCtx)
log.Info("Engine HTTP endpoint close", "url", engineInfo.EngineHttpEndpoint)
if cfg.GRPCHealthCheckEnabled {
healthServer.Shutdown()
}
grpcServer.GracefulStop()
_ = grpcListener.Close()
log.Info("GRPC endpoint closed", "url", grpcEndpoint)
}()
<-ctx.Done()
log.Info("Exiting Engine...")
}
// isWebsocket checks the header of a http request for a websocket upgrade request.
func isWebsocket(r *http.Request) bool {
return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
}
// obtainJWTSecret loads the jwt-secret, either from the provided config,
// or from the default location. If neither of those are present, it generates
// a new secret and stores to the default location.
func obtainJWTSecret(cfg httpcfg.HttpCfg) ([]byte, error) {
// try reading from file
log.Info("Reading JWT secret", "path", cfg.JWTSecretPath)
// If we run the rpcdaemon and datadir is not specified we just use jwt.hex in current directory.
if len(cfg.JWTSecretPath) == 0 {
cfg.JWTSecretPath = "jwt.hex"
}
if data, err := os.ReadFile(cfg.JWTSecretPath); err == nil {
jwtSecret := common.FromHex(strings.TrimSpace(string(data)))
if len(jwtSecret) == 32 {
return jwtSecret, nil
}
log.Error("Invalid JWT secret", "path", cfg.JWTSecretPath, "length", len(jwtSecret))
return nil, errors.New("invalid JWT secret")
}
// Need to generate one
jwtSecret := make([]byte, 32)
rand.Read(jwtSecret)
if err := os.WriteFile(cfg.JWTSecretPath, []byte(hexutil.Encode(jwtSecret)), 0600); err != nil {
return nil, err
}
log.Info("Generated JWT secret", "path", cfg.JWTSecretPath)
return jwtSecret, nil
return nil
}
func createHandler(cfg httpcfg.HttpCfg, apiList []rpc.API, httpHandler http.Handler, wsHandler http.Handler, jwtSecret []byte) (http.Handler, error) {
@ -643,36 +577,8 @@ func createHandler(cfg httpcfg.HttpCfg, apiList []rpc.API, httpHandler http.Hand
return handler, nil
}
func createEngineListener(cfg httpcfg.HttpCfg, engineApi []rpc.API) (*http.Server, *rpc.Server, string, error) {
engineHttpEndpoint := fmt.Sprintf("%s:%d", cfg.AuthRpcHTTPListenAddress, cfg.AuthRpcPort)
engineSrv := rpc.NewServer(cfg.RpcBatchConcurrency, cfg.TraceRequests, true)
if err := node.RegisterApisFromWhitelist(engineApi, nil, engineSrv, true); err != nil {
return nil, nil, "", fmt.Errorf("could not start register RPC engine api: %w", err)
}
jwtSecret, err := obtainJWTSecret(cfg)
if err != nil {
return nil, nil, "", err
}
wsHandler := engineSrv.WebsocketHandler([]string{"*"}, jwtSecret, cfg.WebsocketCompression)
engineHttpHandler := node.NewHTTPHandlerStack(engineSrv, nil /* authCors */, cfg.AuthRpcVirtualHost, cfg.HttpCompression)
engineApiHandler, err := createHandler(cfg, engineApi, engineHttpHandler, wsHandler, jwtSecret)
if err != nil {
return nil, nil, "", err
}
engineListener, _, err := node.StartHTTPEndpoint(engineHttpEndpoint, cfg.AuthRpcTimeouts, engineApiHandler)
if err != nil {
return nil, nil, "", fmt.Errorf("could not start RPC api: %w", err)
}
engineInfo := []interface{}{"url", engineHttpEndpoint, "ws", true, "ws.compression", cfg.WebsocketCompression}
log.Info("HTTP endpoint opened for Engine API", engineInfo...)
return engineListener, engineSrv, engineHttpEndpoint, nil
// isWebsocket checks the header of a http request for a websocket upgrade request.
func isWebsocket(r *http.Request) bool {
return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") &&
strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade")
}

View File

@ -0,0 +1,18 @@
package httpcfg
import (
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli/httpcfg"
)
type HttpCfg struct {
httpcfg.HttpCfg
DisableRpcDaemon bool
OtsServerDisable bool
OtsStaticDir string
OtsExternalAssetUrl string
OtsRpcDaemonUrl string
OtsBeaconApiUrl string
}

View File

@ -10,7 +10,7 @@ import (
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/services"
"github.com/wmitsuda/otterscan/cmd/rpcdaemon/cli/httpcfg"
"github.com/wmitsuda/otterscan/cmd/otter/cli/httpcfg"
erigonHttpcfg "github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli/httpcfg"
)
@ -130,27 +130,3 @@ func APIList(db kv.RoDB, borDb kv.RoDB, eth rpchelper.ApiBackend, txPool txpool.
return list
}
func AuthAPIList(db kv.RoDB, eth rpchelper.ApiBackend, txPool txpool.TxpoolClient, mining txpool.MiningClient,
filters *rpchelper.Filters, stateCache kvcache.Cache, blockReader services.FullBlockReader,
agg *libstate.Aggregator22,
cfg httpcfg.HttpCfg) (list []rpc.API) {
base := commands.NewBaseApi(filters, stateCache, blockReader, agg, cfg.WithDatadir, cfg.EvmCallTimeout)
ethImpl := commands.NewEthAPI(base, db, eth, txPool, mining, cfg.Gascap)
engineImpl := commands.NewEngineAPI(base, db, eth)
list = append(list, rpc.API{
Namespace: "eth",
Public: true,
Service: commands.EthAPI(ethImpl),
Version: "1.0",
}, rpc.API{
Namespace: "engine",
Public: true,
Service: commands.EngineAPI(engineImpl),
Version: "1.0",
})
return list
}

View File

@ -19,13 +19,11 @@ import (
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rpc"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
"github.com/ledgerwatch/erigon/turbo/rpchelper"
"github.com/ledgerwatch/erigon/turbo/transactions"
"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
"github.com/ledgerwatch/log/v3"
)
// API_LEVEL Must be incremented every time new additions are made

View File

@ -3,12 +3,13 @@ package main
import (
"os"
"github.com/go-chi/chi/v5"
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/log/v3"
"github.com/spf13/cobra"
"github.com/wmitsuda/otterscan/cmd/rpcdaemon/cli"
mycmds "github.com/wmitsuda/otterscan/cmd/rpcdaemon/commands"
"github.com/wmitsuda/otterscan/cmd/otter/cli"
mycmds "github.com/wmitsuda/otterscan/cmd/otter/commands"
)
func main() {
@ -26,16 +27,15 @@ func main() {
if borDb != nil {
defer borDb.Close()
}
r := chi.NewRouter()
RouteServer(r, *cfg)
apiList := mycmds.APIList(db, borDb, backend, txPool, mining, ff, stateCache, blockReader, agg, *cfg)
if err := cli.StartRpcServer(ctx, *cfg, apiList, nil); err != nil {
if err := cli.StartRpcServer(ctx, r, *cfg, apiList); err != nil {
log.Error(err.Error())
return nil
}
return nil
}
if err := cmd.ExecuteContext(rootCtx); err != nil {
log.Error(err.Error())
os.Exit(1)

48
cmd/otter/server.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"encoding/json"
"net/http"
"strings"
"gfx.cafe/open/4bytes/sigs"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/wmitsuda/otterscan/cmd/otter/cli/httpcfg"
)
func RouteServer(r chi.Router, cfg httpcfg.HttpCfg) {
r.Group(func(r chi.Router) {
filesDir := http.Dir(cfg.OtsStaticDir)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Handle("/signatures/{hash}", &sigs.HttpServer{})
r.HandleFunc("/config.json", func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]any{
"erigonURL": cfg.OtsRpcDaemonUrl,
"beaconAPI": cfg.OtsBeaconApiUrl,
"assetsURLPrefix": cfg.OtsBeaconApiUrl,
})
})
FileServer(r, "/", filesDir)
})
}
// FileServer conveniently sets up a http.FileServer handler to serve
// static files from a http.FileSystem.
func FileServer(r chi.Router, path string, root http.FileSystem) {
if strings.ContainsAny(path, "{}*") {
panic("FileServer does not permit any URL parameters.")
}
if path != "/" && path[len(path)-1] != '/' {
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
}
path += "*"
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
fs.ServeHTTP(w, r)
})
}

View File

@ -1,52 +0,0 @@
package httpcfg
import (
"github.com/ledgerwatch/erigon-lib/kv/kvcache"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/node/nodecfg/datadir"
"github.com/ledgerwatch/erigon/rpc/rpccfg"
"time"
)
type HttpCfg struct {
Enabled bool
PrivateApiAddr string
WithDatadir bool // Erigon's database can be read by separated processes on same machine - in read-only mode - with full support of transactions. It will share same "OS PageCache" with Erigon process.
DataDir string
Dirs datadir.Dirs
HttpListenAddress string
AuthRpcHTTPListenAddress string
TLSCertfile string
TLSCACert string
TLSKeyFile string
HttpPort int
AuthRpcPort int
HttpCORSDomain []string
HttpVirtualHost []string
AuthRpcVirtualHost []string
HttpCompression bool
API []string
Gascap uint64
MaxTraces uint64
WebsocketEnabled bool
WebsocketCompression bool
RpcAllowListFilePath string
RpcBatchConcurrency uint
RpcStreamingDisable bool
DBReadConcurrency int
TraceCompatibility bool // Bug for bug compatibility for trace_ routines with OpenEthereum
TxPoolApiAddr string
StateCache kvcache.CoherentConfig
Snap ethconfig.Snapshot
Sync ethconfig.Sync
GRPCServerEnabled bool
GRPCListenAddress string
GRPCPort int
GRPCHealthCheckEnabled bool
StarknetGRPCAddress string
JWTSecretPath string // Engine API Authentication
TraceRequests bool // Always trace requests in INFO level
HTTPTimeouts rpccfg.HTTPTimeouts
AuthRpcTimeouts rpccfg.HTTPTimeouts
EvmCallTimeout time.Duration
}

View File

@ -1,72 +0,0 @@
package main
import (
"log"
"net/http"
"os"
"strings"
"time"
"gfx.cafe/open/4bytes/sigs"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
root := "dist"
port := os.Getenv("PORT")
if port == "" {
port = "3001"
}
if os.Getenv("ROOTDIR") != "" {
root = os.Getenv("ROOTDIR")
}
if len(os.Args) > 1 {
if os.Args[1] != "" {
root = os.Args[1]
}
}
filesDir := http.Dir(root)
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.SetHeader("Access-Control-Allow-Origin", "*"))
r.Handle("/signatures/{hash}", &sigs.HttpServer{})
FileServer(r, "/", filesDir)
s := http.Server{
Addr: ":" + port,
Handler: r,
}
s.SetKeepAlivesEnabled(false)
s.ReadHeaderTimeout = 250 * time.Millisecond
s.MaxHeaderBytes = 8192
log.Println("static asset server running on " + port)
// Start the server.
if err := s.ListenAndServe(); err != nil {
log.Fatalf("error in ListenAndServe: %v", err)
}
}
// FileServer conveniently sets up a http.FileServer handler to serve
// static files from a http.FileSystem.
func FileServer(r chi.Router, path string, root http.FileSystem) {
if strings.ContainsAny(path, "{}*") {
panic("FileServer does not permit any URL parameters.")
}
if path != "/" && path[len(path)-1] != '/' {
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
}
path += "*"
r.Get(path, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
fs := http.StripPrefix(pathPrefix, http.FileServer(root))
fs.ServeHTTP(w, r)
})
}

View File

@ -2,11 +2,11 @@ version: '3.9'
services:
rpcdaemon:
otterscan:
build:
context: .
command: |
rpcdaemon
otter
--http.addr=0.0.0.0 --http.vhosts=* --http.corsdomain=* --ws
--http.api=eth,erigon,web3,net,trace,debug,txpool,ots
--metrics --metrics.addr=0.0.0.0 --metrics.port=6064
@ -14,11 +14,4 @@ services:
--private.api.addr=172.105.22.234:9191
##--datadir=/erigon
ports:
- 8545:8545
server:
build:
context: .
command: |
server
ports:
- 3001:3001
- 3000:8545

View File

@ -1,5 +0,0 @@
{
"erigonURL": "http://localhost:8545",
"beaconAPI": "http://localhost:3500",
"assetsURLPrefix": "/"
}