docker compose
This commit is contained in:
parent
6b794b24db
commit
454fc0a576
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"erigonURL": "http://localhost:8545",
|
||||
"beaconAPI": "http://localhost:3500",
|
||||
"assetsURLPrefix": "/"
|
||||
}
|
Loading…
Reference in New Issue