forked from cerc-io/laconicd-deprecated
conflicts
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultJSONRPCAddress is the default address the JSON-RPC server binds to.
|
||||
DefaultJSONRPCAddress = "tcp://0.0.0.0:8545"
|
||||
// DefaultEthereumWebsocketAddress is the default address the Ethereum websocket server binds to.
|
||||
DefaultEthereumWebsocketAddress = "tcp://0.0.0.0:8546"
|
||||
)
|
||||
|
||||
// Config defines the server's top level configuration
|
||||
type Config struct {
|
||||
*config.Config
|
||||
|
||||
JSONRPC JSONRPCConfig `mapstructure:"json-rpc"`
|
||||
EthereumWebsocket WebsocketConfig `mapstructure:"ethereum-websocket"`
|
||||
}
|
||||
|
||||
// JSONRPCConfig defines the Ethereum API listener configuration.
|
||||
type JSONRPCConfig struct {
|
||||
// Enable defines if the JSON-RPC server should be enabled.
|
||||
Enable bool `mapstructure:"enable"`
|
||||
// Address defines the JSON-RPC server address to listen on
|
||||
Address string `mapstructure:"address"`
|
||||
}
|
||||
|
||||
// WebsocketConfig defines the Ethereum API listener configuration.
|
||||
type WebsocketConfig struct {
|
||||
// Enable defines if the Ethereum websocker server should be enabled.
|
||||
Enable bool `mapstructure:"enable"`
|
||||
|
||||
// Address defines the Websocket server address to listen on
|
||||
Address string `mapstructure:"address"`
|
||||
}
|
||||
|
||||
// DefaultConfig returns server's default configuration.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Config: config.DefaultConfig(),
|
||||
JSONRPC: JSONRPCConfig{
|
||||
Enable: true,
|
||||
Address: DefaultJSONRPCAddress,
|
||||
},
|
||||
EthereumWebsocket: WebsocketConfig{
|
||||
Enable: true,
|
||||
Address: DefaultEthereumWebsocketAddress,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetConfig returns a fully parsed Config object.
|
||||
func GetConfig(v *viper.Viper) Config {
|
||||
sdkConfig := config.GetConfig(v)
|
||||
return Config{
|
||||
Config: &sdkConfig,
|
||||
JSONRPC: JSONRPCConfig{
|
||||
Enable: v.GetBool("json-rpc.enable"),
|
||||
Address: v.GetString("json-rpc.address"),
|
||||
},
|
||||
EthereumWebsocket: WebsocketConfig{
|
||||
Enable: v.GetBool("ethereum-websocket.enable"),
|
||||
Address: v.GetString("ethereum-websocket.address"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
)
|
||||
|
||||
const defaultConfigTemplate = `# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Base Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The minimum gas prices a validator is willing to accept for processing a
|
||||
# transaction. A transaction's fees must meet the minimum of any denomination
|
||||
# specified in this config (e.g. 0.25token1;0.0001token2).
|
||||
minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}"
|
||||
|
||||
# default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
|
||||
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
|
||||
# everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals
|
||||
# custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
|
||||
pruning = "{{ .BaseConfig.Pruning }}"
|
||||
|
||||
# These are applied if and only if the pruning strategy is custom.
|
||||
pruning-keep-recent = "{{ .BaseConfig.PruningKeepRecent }}"
|
||||
pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}"
|
||||
pruning-interval = "{{ .BaseConfig.PruningInterval }}"
|
||||
|
||||
# HaltHeight contains a non-zero block height at which a node will gracefully
|
||||
# halt and shutdown that can be used to assist upgrades and testing.
|
||||
#
|
||||
# Note: Commitment of state will be attempted on the corresponding block.
|
||||
halt-height = {{ .BaseConfig.HaltHeight }}
|
||||
|
||||
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
|
||||
# a node will gracefully halt and shutdown that can be used to assist upgrades
|
||||
# and testing.
|
||||
#
|
||||
# Note: Commitment of state will be attempted on the corresponding block.
|
||||
halt-time = {{ .BaseConfig.HaltTime }}
|
||||
|
||||
# MinRetainBlocks defines the minimum block height offset from the current
|
||||
# block being committed, such that all blocks past this offset are pruned
|
||||
# from Tendermint. It is used as part of the process of determining the
|
||||
# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates
|
||||
# that no blocks should be pruned.
|
||||
#
|
||||
# This configuration value is only responsible for pruning Tendermint blocks.
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: Tendermint block pruning is dependant on this parameter in conunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
min-retain-blocks = {{ .BaseConfig.MinRetainBlocks }}
|
||||
|
||||
# InterBlockCache enables inter-block caching.
|
||||
inter-block-cache = {{ .BaseConfig.InterBlockCache }}
|
||||
|
||||
# IndexEvents defines the set of events in the form {eventType}.{attributeKey},
|
||||
# which informs Tendermint what to index. If empty, all events will be indexed.
|
||||
#
|
||||
# Example:
|
||||
# ["message.sender", "message.recipient"]
|
||||
index-events = {{ .BaseConfig.IndexEvents }}
|
||||
|
||||
###############################################################################
|
||||
### Telemetry Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[telemetry]
|
||||
|
||||
# Prefixed with keys to separate services.
|
||||
service-name = "{{ .Telemetry.ServiceName }}"
|
||||
|
||||
# Enabled enables the application telemetry functionality. When enabled,
|
||||
# an in-memory sink is also enabled by default. Operators may also enabled
|
||||
# other sinks such as Prometheus.
|
||||
enabled = {{ .Telemetry.Enabled }}
|
||||
|
||||
# Enable prefixing gauge values with hostname.
|
||||
enable-hostname = {{ .Telemetry.EnableHostname }}
|
||||
|
||||
# Enable adding hostname to labels.
|
||||
enable-hostname-label = {{ .Telemetry.EnableHostnameLabel }}
|
||||
|
||||
# Enable adding service to labels.
|
||||
enable-service-label = {{ .Telemetry.EnableServiceLabel }}
|
||||
|
||||
# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink.
|
||||
prometheus-retention-time = {{ .Telemetry.PrometheusRetentionTime }}
|
||||
|
||||
# GlobalLabels defines a global set of name/value label tuples applied to all
|
||||
# metrics emitted using the wrapper functions defined in telemetry package.
|
||||
#
|
||||
# Example:
|
||||
# [["chain_id", "cosmoshub-1"]]
|
||||
global-labels = [{{ range $k, $v := .Telemetry.GlobalLabels }}
|
||||
["{{index $v 0 }}", "{{ index $v 1}}"],{{ end }}
|
||||
]
|
||||
|
||||
###############################################################################
|
||||
### API Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[api]
|
||||
|
||||
# Enable defines if the API server should be enabled.
|
||||
enable = {{ .API.Enable }}
|
||||
|
||||
# Swagger defines if swagger documentation should automatically be registered.
|
||||
swagger = {{ .API.Swagger }}
|
||||
|
||||
# Address defines the API server to listen on.
|
||||
address = "{{ .API.Address }}"
|
||||
|
||||
# MaxOpenConnections defines the number of maximum open connections.
|
||||
max-open-connections = {{ .API.MaxOpenConnections }}
|
||||
|
||||
# RPCReadTimeout defines the Tendermint RPC read timeout (in seconds).
|
||||
rpc-read-timeout = {{ .API.RPCReadTimeout }}
|
||||
|
||||
# RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds).
|
||||
rpc-write-timeout = {{ .API.RPCWriteTimeout }}
|
||||
|
||||
# RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes).
|
||||
rpc-max-body-bytes = {{ .API.RPCMaxBodyBytes }}
|
||||
|
||||
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
|
||||
enabled-unsafe-cors = {{ .API.EnableUnsafeCORS }}
|
||||
|
||||
###############################################################################
|
||||
### gRPC Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[grpc]
|
||||
|
||||
# Enable defines if the gRPC server should be enabled.
|
||||
enable = {{ .GRPC.Enable }}
|
||||
|
||||
# Address defines the gRPC server address to bind to.
|
||||
address = "{{ .GRPC.Address }}"
|
||||
|
||||
###############################################################################
|
||||
### State Sync Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# State sync snapshots allow other nodes to rapidly join the network without replaying historical
|
||||
# blocks, instead downloading and applying a snapshot of the application state at a given height.
|
||||
[state-sync]
|
||||
|
||||
# snapshot-interval specifies the block interval at which local state sync snapshots are
|
||||
# taken (0 to disable). Must be a multiple of pruning-keep-every.
|
||||
snapshot-interval = {{ .StateSync.SnapshotInterval }}
|
||||
|
||||
# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).
|
||||
snapshot-keep-recent = {{ .StateSync.SnapshotKeepRecent }}
|
||||
|
||||
###############################################################################
|
||||
### JSON-RPC Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# JSON-RPC enables a JSON-RPC server that allows Ethereum clients to connect to an Ethermint node.
|
||||
# For the full list of supported endpoints see https://docs.ethermint.zone/basics/json_rpc.html
|
||||
[json-rpc]
|
||||
|
||||
# Enable defines if the JSON-RPC server should be enabled.
|
||||
enable = {{ .JSONRPC.Enable }}
|
||||
|
||||
# Address defines the JSON-RPC server address to bind to.
|
||||
address = "{{ .JSONRPC.Address }}"
|
||||
|
||||
|
||||
###############################################################################
|
||||
### Ethereum Websocket Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# Ethereum Websocket enables a PubSub server that allows Ethereum clients to subscribe to specific endpoints.
|
||||
[ethereum-websocket]
|
||||
|
||||
# Enable defines if the Ethereum Websocket server should be enabled.
|
||||
enable = {{ .EthereumWebsocket.Enable }}
|
||||
|
||||
# Address defines the Ethereum Websocket server address to bind to.
|
||||
address = "{{ .EthereumWebsocket.Address }}"
|
||||
`
|
||||
|
||||
var configTemplate *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
tmpl := template.New("appConfigFileTemplate")
|
||||
|
||||
if configTemplate, err = tmpl.Parse(defaultConfigTemplate); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig retrieves the default environment configuration for the
|
||||
// application.
|
||||
func ParseConfig(v *viper.Viper) (*Config, error) {
|
||||
conf := DefaultConfig()
|
||||
err := v.Unmarshal(conf)
|
||||
|
||||
return conf, err
|
||||
}
|
||||
|
||||
// WriteConfigFile renders config using the template and writes it to
|
||||
// configFilePath.
|
||||
func WriteConfigFile(configFilePath string, config *Config) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
if err := configTemplate.Execute(&buffer, config); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tmos.MustWriteFile(configFilePath, buffer.Bytes(), 0644)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package server
|
||||
|
||||
// Tendermint full-node start flags
|
||||
const (
|
||||
flagWithTendermint = "with-tendermint"
|
||||
flagAddress = "address"
|
||||
flagTransport = "transport"
|
||||
flagTraceStore = "trace-store"
|
||||
flagCPUProfile = "cpu-profile"
|
||||
)
|
||||
|
||||
// GRPC-related flags.
|
||||
const (
|
||||
flagGRPCEnable = "grpc.enable"
|
||||
flagGRPCAddress = "grpc.address"
|
||||
)
|
||||
|
||||
// RPCAPI-related flags.
|
||||
const (
|
||||
flagRPCAPI = "rpc-api"
|
||||
)
|
||||
|
||||
// Ethereum-related flags.
|
||||
const (
|
||||
flagJSONRPCEnable = "json-rpc.enable"
|
||||
flagJSONRPCAddress = "json-rpc.address"
|
||||
flagEthereumWebsocketEnable = "ethereum-websocket.enable"
|
||||
flagEthereumWebsocketAddress = "ethereum-websocket.address"
|
||||
)
|
||||
@@ -0,0 +1,90 @@
|
||||
package jsonrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/ethermint/server/config"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
rpcServer *rpc.Server
|
||||
apis []rpc.API
|
||||
http *http.Server
|
||||
}
|
||||
|
||||
// NewService creates a new JSON-RPC server instance over http with public Ethereum APIs
|
||||
func NewService(apis []rpc.API) *Service {
|
||||
s := &Service{
|
||||
rpcServer: rpc.NewServer(),
|
||||
apis: apis,
|
||||
http: &http.Server{},
|
||||
}
|
||||
s.http.Handler = s.rpcServer
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Name returns the JSON-RPC service name
|
||||
func (Service) Name() string {
|
||||
return "JSON-RPC"
|
||||
}
|
||||
|
||||
// RegisterRoutes registers the JSON-RPC server to the application. It fails if any of the
|
||||
// API names fail to register.
|
||||
func (s *Service) RegisterRoutes() error {
|
||||
for _, api := range s.apis {
|
||||
if err := s.rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the JSON-RPC server on the address defined on the configuration.
|
||||
func (s *Service) Start(cfg config.Config) error {
|
||||
u, err := url.Parse(cfg.JSONRPC.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", u.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
err = s.http.Serve(listener)
|
||||
if err != nil {
|
||||
errCh <- fmt.Errorf("failed to serve: %w", err)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the JSON-RPC service by no longer reading new requests, waits for
|
||||
// stopPendingRequestTimeout to allow pending requests to finish, then closes all codecs which will
|
||||
// cancel pending requests and subscriptions.
|
||||
func (s *Service) Stop() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err := s.http.Shutdown(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/ethermint/rpc/websockets"
|
||||
"github.com/cosmos/ethermint/server/config"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
websocketServer *websockets.Server
|
||||
}
|
||||
|
||||
// NewService creates a new gRPC server instance with a defined listener address.
|
||||
func NewService(clientCtx client.Context) *Service {
|
||||
return &Service{
|
||||
websocketServer: websockets.NewServer(clientCtx),
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the JSON-RPC service name
|
||||
func (Service) Name() string {
|
||||
return "Ethereum Websocket"
|
||||
}
|
||||
|
||||
// Start runs the websocket server
|
||||
func (s Service) Start(cfg config.Config) error {
|
||||
s.websocketServer.Address = cfg.JSONRPC.Address
|
||||
|
||||
u, err := url.Parse(cfg.EthereumWebsocket.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ws := mux.NewRouter()
|
||||
ws.Handle("/", s.websocketServer)
|
||||
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
err := http.ListenAndServe(":"+u.Port(), ws)
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
package server
|
||||
|
||||
// DONTCOVER
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/rpc/client/local"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
sdkserver "github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/cosmos/cosmos-sdk/server/api"
|
||||
sdkconfig "github.com/cosmos/cosmos-sdk/server/config"
|
||||
servergrpc "github.com/cosmos/cosmos-sdk/server/grpc"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
||||
"github.com/tendermint/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/node"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
|
||||
"github.com/cosmos/ethermint/rpc"
|
||||
"github.com/cosmos/ethermint/server/config"
|
||||
"github.com/cosmos/ethermint/server/services/jsonrpc"
|
||||
"github.com/cosmos/ethermint/server/services/websocket"
|
||||
)
|
||||
|
||||
// StartCmd runs the service passed in, either stand-alone or in-process with
|
||||
// Tendermint.
|
||||
func StartCmd(appCreator servertypes.AppCreator, defaultNodeHome string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Run the full node",
|
||||
Long: `Run the full node application with Tendermint in or out of process. By
|
||||
default, the application will run with Tendermint in process.
|
||||
|
||||
Pruning options can be provided via the '--pruning' flag or alternatively with '--pruning-keep-recent',
|
||||
'pruning-keep-every', and 'pruning-interval' together.
|
||||
|
||||
For '--pruning' the options are as follows:
|
||||
|
||||
default: the last 100 states are kept in addition to every 500th state; pruning at 10 block intervals
|
||||
nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
|
||||
everything: all saved states will be deleted, storing only the current state; pruning at 10 block intervals
|
||||
custom: allow pruning options to be manually specified through 'pruning-keep-recent', 'pruning-keep-every', and 'pruning-interval'
|
||||
|
||||
Node halting configurations exist in the form of two flags: '--halt-height' and '--halt-time'. During
|
||||
the ABCI Commit phase, the node will check if the current block height is greater than or equal to
|
||||
the halt-height or if the current block time is greater than or equal to the halt-time. If so, the
|
||||
node will attempt to gracefully shutdown and the block will not be committed. In addition, the node
|
||||
will not be able to commit subsequent blocks.
|
||||
|
||||
For profiling and benchmarking purposes, CPU profiling can be enabled via the '--cpu-profile' flag
|
||||
which accepts a path for the resulting pprof file.
|
||||
`,
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
serverCtx := sdkserver.GetServerContextFromCmd(cmd)
|
||||
|
||||
// Bind flags to the Context's Viper so the app construction can set
|
||||
// options accordingly.
|
||||
if err := serverCtx.Viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := sdkserver.GetPruningOptionsFromFlags(serverCtx.Viper)
|
||||
return err
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
serverCtx := sdkserver.GetServerContextFromCmd(cmd)
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
|
||||
withTM, _ := cmd.Flags().GetBool(flagWithTendermint)
|
||||
if !withTM {
|
||||
serverCtx.Logger.Info("starting ABCI without Tendermint")
|
||||
return startStandAlone(serverCtx, appCreator)
|
||||
}
|
||||
|
||||
serverCtx.Logger.Info("starting ABCI with Tendermint")
|
||||
|
||||
// amino is needed here for backwards compatibility of REST routes
|
||||
err := startInProcess(serverCtx, clientCtx, appCreator)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
|
||||
cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint")
|
||||
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
|
||||
cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc")
|
||||
cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file")
|
||||
cmd.Flags().String(sdkserver.FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)")
|
||||
cmd.Flags().IntSlice(sdkserver.FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary")
|
||||
cmd.Flags().Uint64(sdkserver.FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node")
|
||||
cmd.Flags().Uint64(sdkserver.FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
|
||||
cmd.Flags().Bool(sdkserver.FlagInterBlockCache, true, "Enable inter-block caching")
|
||||
cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file")
|
||||
cmd.Flags().Bool(sdkserver.FlagTrace, false, "Provide full stack traces for errors in ABCI Log")
|
||||
cmd.Flags().String(sdkserver.FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)")
|
||||
cmd.Flags().Uint64(sdkserver.FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')")
|
||||
cmd.Flags().Uint64(sdkserver.FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')")
|
||||
cmd.Flags().Uint64(sdkserver.FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')")
|
||||
cmd.Flags().Uint(sdkserver.FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks")
|
||||
cmd.Flags().Uint64(sdkserver.FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks")
|
||||
|
||||
cmd.Flags().Bool(flagGRPCEnable, true, "Define if the gRPC server should be enabled")
|
||||
cmd.Flags().String(flagGRPCAddress, sdkconfig.DefaultGRPCAddress, "the gRPC server address to listen on")
|
||||
|
||||
cmd.Flags().Bool(flagJSONRPCEnable, true, "Define if the Ethereum JSON-RPC server should be enabled")
|
||||
cmd.Flags().String(flagJSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
|
||||
|
||||
cmd.Flags().Bool(flagEthereumWebsocketEnable, true, "Define if the Ethereum Websocket server should be enabled")
|
||||
cmd.Flags().String(flagEthereumWebsocketAddress, config.DefaultEthereumWebsocketAddress, "the Ethereum websocket server address to listen on")
|
||||
|
||||
cmd.Flags().Uint64(sdkserver.FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
|
||||
cmd.Flags().Uint32(sdkserver.FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")
|
||||
|
||||
cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
|
||||
|
||||
cmd.Flags().String(flagRPCAPI, "", fmt.Sprintf("Comma separated list of RPC API modules to enable: %s, %s, %s, %s", rpc.Web3Namespace, rpc.EthNamespace, rpc.PersonalNamespace, rpc.NetNamespace))
|
||||
|
||||
// add support for all Tendermint-specific command line options
|
||||
tcmd.AddNodeFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startStandAlone(ctx *sdkserver.Context, appCreator servertypes.AppCreator) error {
|
||||
addr := ctx.Viper.GetString(flagAddress)
|
||||
transport := ctx.Viper.GetString(flagTransport)
|
||||
home := ctx.Viper.GetString(flags.FlagHome)
|
||||
|
||||
db, err := openDB(home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
traceWriterFile := ctx.Viper.GetString(flagTraceStore)
|
||||
traceWriter, err := openTraceWriter(traceWriterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper)
|
||||
|
||||
svr, err := server.NewServer(addr, transport, app)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating listener: %v", err)
|
||||
}
|
||||
|
||||
svr.SetLogger(ctx.Logger.With("module", "abci-server"))
|
||||
|
||||
err = svr.Start()
|
||||
if err != nil {
|
||||
tmos.Exit(err.Error())
|
||||
}
|
||||
|
||||
sdkserver.TrapSignal(func() {
|
||||
if err = svr.Stop(); err != nil {
|
||||
tmos.Exit(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// run forever (the node will not be returned)
|
||||
select {}
|
||||
}
|
||||
|
||||
// legacyAminoCdc is used for the legacy REST API
|
||||
func startInProcess(ctx *sdkserver.Context, clientCtx client.Context, appCreator servertypes.AppCreator) error {
|
||||
cfg := ctx.Config
|
||||
home := cfg.RootDir
|
||||
var cpuProfileCleanup func()
|
||||
|
||||
if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" {
|
||||
f, err := os.Create(cpuProfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Logger.Info("starting CPU profiler", "profile", cpuProfile)
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cpuProfileCleanup = func() {
|
||||
ctx.Logger.Info("stopping CPU profiler", "profile", cpuProfile)
|
||||
pprof.StopCPUProfile()
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
traceWriterFile := ctx.Viper.GetString(flagTraceStore)
|
||||
db, err := openDB(home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
traceWriter, err := openTraceWriter(traceWriterFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper)
|
||||
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
genDocProvider := node.DefaultGenesisDocProviderFunc(cfg)
|
||||
tmNode, err := node.NewNode(
|
||||
cfg,
|
||||
pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()),
|
||||
nodeKey,
|
||||
proxy.NewLocalClientCreator(app),
|
||||
genDocProvider,
|
||||
node.DefaultDBProvider,
|
||||
node.DefaultMetricsProvider(cfg.Instrumentation),
|
||||
ctx.Logger.With("module", "node"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Logger.Debug("Initialization: tmNode created")
|
||||
|
||||
if err := tmNode.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.Logger.Debug("Initialization: tmNode started")
|
||||
|
||||
// Add the tx service to the gRPC router.
|
||||
app.RegisterTxService(clientCtx)
|
||||
|
||||
var apiSrv *api.Server
|
||||
|
||||
config := config.GetConfig(ctx.Viper)
|
||||
|
||||
genDoc, err := genDocProvider()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientCtx = clientCtx.
|
||||
WithHomeDir(home).
|
||||
WithChainID(genDoc.ChainID).
|
||||
WithClient(local.New(tmNode))
|
||||
|
||||
if config.API.Enable {
|
||||
apiSrv = api.New(clientCtx, ctx.Logger.With("module", "api-server"))
|
||||
app.RegisterAPIRoutes(apiSrv, config.API)
|
||||
errCh := make(chan error)
|
||||
|
||||
go func() {
|
||||
if err := apiSrv.Start(*config.Config); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
}
|
||||
}
|
||||
|
||||
var grpcSrv *grpc.Server
|
||||
if config.GRPC.Enable {
|
||||
grpcSrv, err = servergrpc.StartGRPCServer(clientCtx, app, config.GRPC.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
rpcapi := ctx.Viper.GetString(flagRPCAPI)
|
||||
rpcapi = strings.ReplaceAll(rpcapi, " ", "")
|
||||
rpcapiArr := strings.Split(rpcapi, ",")
|
||||
|
||||
jsonRPCSrv := jsonrpc.NewService(rpc.GetAPIs(clientCtx, rpcapiArr))
|
||||
if config.JSONRPC.Enable {
|
||||
if err := jsonRPCSrv.RegisterRoutes(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = jsonRPCSrv.Start(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if config.EthereumWebsocket.Enable {
|
||||
websocketSrv := websocket.NewService(clientCtx)
|
||||
err = websocketSrv.Start(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tmNode.IsRunning() {
|
||||
_ = tmNode.Stop()
|
||||
}
|
||||
|
||||
if cpuProfileCleanup != nil {
|
||||
cpuProfileCleanup()
|
||||
}
|
||||
|
||||
if apiSrv != nil {
|
||||
_ = apiSrv.Close()
|
||||
}
|
||||
|
||||
if grpcSrv != nil {
|
||||
grpcSrv.Stop()
|
||||
}
|
||||
|
||||
_ = jsonRPCSrv.Stop()
|
||||
|
||||
ctx.Logger.Info("exiting...")
|
||||
}()
|
||||
|
||||
// Wait for SIGINT or SIGTERM signal
|
||||
return sdkserver.WaitForQuitSignals()
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
sdkserver "github.com/cosmos/cosmos-sdk/server"
|
||||
servertypes "github.com/cosmos/cosmos-sdk/server/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
|
||||
"github.com/cosmos/ethermint/server/config"
|
||||
)
|
||||
|
||||
// InterceptConfigsPreRunHandler performs a pre-run function for the root daemon
|
||||
// application command. It will create a Viper literal and a default server
|
||||
// Context. The server Tendermint configuration will either be read and parsed
|
||||
// or created and saved to disk, where the server Context is updated to reflect
|
||||
// the Tendermint configuration. The Viper literal is used to read and parse
|
||||
// the application configuration. Command handlers can fetch the server Context
|
||||
// to get the Tendermint configuration or to get access to Viper.
|
||||
func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {
|
||||
serverCtx := sdkserver.NewDefaultContext()
|
||||
|
||||
// Get the executable name and configure the viper instance so that environmental
|
||||
// variables are checked based off that name. The underscore character is used
|
||||
// as a separator
|
||||
executableName, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
basename := path.Base(executableName)
|
||||
|
||||
// Configure the viper instance
|
||||
if err := serverCtx.Viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := serverCtx.Viper.BindPFlags(cmd.PersistentFlags()); err != nil {
|
||||
return err
|
||||
}
|
||||
serverCtx.Viper.SetEnvPrefix(basename)
|
||||
serverCtx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
serverCtx.Viper.AutomaticEnv()
|
||||
|
||||
// intercept configuration files, using both Viper instances separately
|
||||
config, err := interceptConfigs(serverCtx.Viper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// return value is a tendermint configuration object
|
||||
serverCtx.Config = config
|
||||
|
||||
var logWriter io.Writer
|
||||
if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain {
|
||||
logWriter = zerolog.ConsoleWriter{Out: os.Stderr}
|
||||
} else {
|
||||
logWriter = os.Stderr
|
||||
}
|
||||
|
||||
logLvlStr := serverCtx.Viper.GetString(flags.FlagLogLevel)
|
||||
logLvl, err := zerolog.ParseLevel(logLvlStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse log level (%s): %w", logLvlStr, err)
|
||||
}
|
||||
|
||||
serverCtx.Logger = sdkserver.ZeroLogWrapper{
|
||||
Logger: zerolog.New(logWriter).Level(logLvl).With().Timestamp().Logger(),
|
||||
}
|
||||
|
||||
return sdkserver.SetCmdServerContext(cmd, serverCtx)
|
||||
}
|
||||
|
||||
// interceptConfigs parses and updates a Tendermint configuration file or
|
||||
// creates a new one and saves it. It also parses and saves the application
|
||||
// configuration file. The Tendermint configuration file is parsed given a root
|
||||
// Viper object, whereas the application is parsed with the private package-aware
|
||||
// viperCfg object.
|
||||
func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) {
|
||||
rootDir := rootViper.GetString(flags.FlagHome)
|
||||
configPath := filepath.Join(rootDir, "config")
|
||||
configFile := filepath.Join(configPath, "config.toml")
|
||||
|
||||
conf := tmcfg.DefaultConfig()
|
||||
|
||||
switch _, err := os.Stat(configFile); {
|
||||
case os.IsNotExist(err):
|
||||
tmcfg.EnsureRoot(rootDir)
|
||||
|
||||
if err = conf.ValidateBasic(); err != nil {
|
||||
return nil, fmt.Errorf("error in config file: %v", err)
|
||||
}
|
||||
|
||||
conf.RPC.PprofListenAddress = "localhost:6060"
|
||||
conf.P2P.RecvRate = 5120000
|
||||
conf.P2P.SendRate = 5120000
|
||||
conf.Consensus.TimeoutCommit = 5 * time.Second
|
||||
tmcfg.WriteConfigFile(configFile, conf)
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
||||
default:
|
||||
rootViper.SetConfigType("toml")
|
||||
rootViper.SetConfigName("config")
|
||||
rootViper.AddConfigPath(configPath)
|
||||
if err := rootViper.ReadInConfig(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read in app.toml: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Read into the configuration whatever data the viper instance has for it
|
||||
// This may come from the configuration file above but also any of the other sources
|
||||
// viper uses
|
||||
if err := rootViper.Unmarshal(conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.SetRoot(rootDir)
|
||||
|
||||
appConfigFilePath := filepath.Join(configPath, "app.toml")
|
||||
if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) {
|
||||
appConf, err := config.ParseConfig(rootViper)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse app.toml: %w", err)
|
||||
}
|
||||
|
||||
config.WriteConfigFile(appConfigFilePath, appConf)
|
||||
}
|
||||
|
||||
rootViper.SetConfigType("toml")
|
||||
rootViper.SetConfigName("app")
|
||||
rootViper.AddConfigPath(configPath)
|
||||
if err := rootViper.ReadInConfig(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read in app.toml: %w", err)
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// AddCommands adds the server commands
|
||||
func AddCommands(
|
||||
rootCmd *cobra.Command, defaultNodeHome string,
|
||||
appCreator servertypes.AppCreator, appExport servertypes.AppExporter, addStartFlags servertypes.ModuleInitFlags,
|
||||
) {
|
||||
tendermintCmd := &cobra.Command{
|
||||
Use: "tendermint",
|
||||
Short: "Tendermint subcommands",
|
||||
}
|
||||
|
||||
tendermintCmd.AddCommand(
|
||||
sdkserver.ShowNodeIDCmd(),
|
||||
sdkserver.ShowValidatorCmd(),
|
||||
sdkserver.ShowAddressCmd(),
|
||||
sdkserver.VersionCmd(),
|
||||
)
|
||||
|
||||
startCmd := StartCmd(appCreator, defaultNodeHome)
|
||||
addStartFlags(startCmd)
|
||||
|
||||
rootCmd.AddCommand(
|
||||
startCmd,
|
||||
sdkserver.UnsafeResetAllCmd(),
|
||||
flags.LineBreak,
|
||||
tendermintCmd,
|
||||
sdkserver.ExportCmd(appExport, defaultNodeHome),
|
||||
flags.LineBreak,
|
||||
version.NewVersionCommand(),
|
||||
)
|
||||
}
|
||||
|
||||
func openDB(rootDir string) (dbm.DB, error) {
|
||||
dataDir := filepath.Join(rootDir, "data")
|
||||
return sdk.NewLevelDB("application", dataDir)
|
||||
}
|
||||
|
||||
func openTraceWriter(traceWriterFile string) (w io.Writer, err error) {
|
||||
if traceWriterFile == "" {
|
||||
return
|
||||
}
|
||||
return os.OpenFile(
|
||||
traceWriterFile,
|
||||
os.O_WRONLY|os.O_APPEND|os.O_CREATE,
|
||||
0666,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user