2018-04-05 03:09:02 -07:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2018-04-21 19:26:46 -07:00
|
|
|
"encoding/json"
|
2018-04-23 12:15:50 -07:00
|
|
|
"net"
|
2018-04-05 03:09:02 -07:00
|
|
|
"os"
|
2018-11-02 06:44:40 -07:00
|
|
|
"os/signal"
|
2018-06-25 09:33:07 -07:00
|
|
|
"path/filepath"
|
2018-11-02 06:44:40 -07:00
|
|
|
"syscall"
|
2018-11-09 16:08:35 -08:00
|
|
|
"time"
|
2018-04-05 03:09:02 -07:00
|
|
|
|
2018-04-23 12:15:50 -07:00
|
|
|
"github.com/pkg/errors"
|
2018-04-05 03:09:02 -07:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
|
|
|
|
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
|
|
|
cfg "github.com/tendermint/tendermint/config"
|
2018-07-02 13:34:06 -07:00
|
|
|
"github.com/tendermint/tendermint/libs/cli"
|
|
|
|
tmflags "github.com/tendermint/tendermint/libs/cli/flags"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
2018-12-10 06:27:25 -08:00
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
|
|
"github.com/cosmos/cosmos-sdk/server/config"
|
|
|
|
"github.com/cosmos/cosmos-sdk/version"
|
2018-04-05 03:09:02 -07:00
|
|
|
)
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// server context
|
2018-04-05 03:31:33 -07:00
|
|
|
type Context struct {
|
|
|
|
Config *cfg.Config
|
|
|
|
Logger log.Logger
|
|
|
|
}
|
|
|
|
|
2018-04-05 04:16:20 -07:00
|
|
|
func NewDefaultContext() *Context {
|
|
|
|
return NewContext(
|
|
|
|
cfg.DefaultConfig(),
|
|
|
|
log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-04-05 03:31:33 -07:00
|
|
|
func NewContext(config *cfg.Config, logger log.Logger) *Context {
|
|
|
|
return &Context{config, logger}
|
|
|
|
}
|
|
|
|
|
2018-04-21 19:26:46 -07:00
|
|
|
//___________________________________________________________________________________
|
2018-04-05 03:31:33 -07:00
|
|
|
|
2018-04-05 03:09:02 -07:00
|
|
|
// PersistentPreRunEFn returns a PersistentPreRunE function for cobra
|
|
|
|
// that initailizes the passed in context with a properly configured
|
2018-10-10 15:45:41 -07:00
|
|
|
// logger and config object.
|
2018-04-05 03:09:02 -07:00
|
|
|
func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error {
|
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
|
|
|
if cmd.Name() == version.VersionCmd.Name() {
|
|
|
|
return nil
|
|
|
|
}
|
2018-06-25 09:33:07 -07:00
|
|
|
config, err := interceptLoadConfig()
|
2018-04-05 03:09:02 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-09-11 02:15:38 -07:00
|
|
|
err = validateConfig(config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-05 03:09:02 -07:00
|
|
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
|
|
|
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if viper.GetBool(cli.TraceFlag) {
|
|
|
|
logger = log.NewTracingLogger(logger)
|
|
|
|
}
|
|
|
|
logger = logger.With("module", "main")
|
|
|
|
context.Config = config
|
|
|
|
context.Logger = logger
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2018-06-25 09:33:07 -07:00
|
|
|
|
|
|
|
// If a new config is created, change some of the default tendermint settings
|
|
|
|
func interceptLoadConfig() (conf *cfg.Config, err error) {
|
|
|
|
tmpConf := cfg.DefaultConfig()
|
2018-06-28 15:52:10 -07:00
|
|
|
err = viper.Unmarshal(tmpConf)
|
|
|
|
if err != nil {
|
|
|
|
// TODO: Handle with #870
|
|
|
|
panic(err)
|
|
|
|
}
|
2018-06-25 09:33:07 -07:00
|
|
|
rootDir := tmpConf.RootDir
|
|
|
|
configFilePath := filepath.Join(rootDir, "config/config.toml")
|
|
|
|
// Intercept only if the file doesn't already exist
|
2018-07-16 09:43:10 -07:00
|
|
|
|
2018-06-25 09:33:07 -07:00
|
|
|
if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
|
|
|
|
// the following parse config is needed to create directories
|
2018-10-10 15:45:41 -07:00
|
|
|
conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
|
2018-07-16 09:43:10 -07:00
|
|
|
conf.ProfListenAddress = "localhost:6060"
|
|
|
|
conf.P2P.RecvRate = 5120000
|
|
|
|
conf.P2P.SendRate = 5120000
|
2018-08-10 06:40:21 -07:00
|
|
|
conf.TxIndex.IndexAllTags = true
|
2018-11-09 16:08:35 -08:00
|
|
|
conf.Consensus.TimeoutCommit = 5 * time.Second
|
2018-07-16 09:43:10 -07:00
|
|
|
cfg.WriteConfigFile(configFilePath, conf)
|
2018-06-25 09:33:07 -07:00
|
|
|
// Fall through, just so that its parsed into memory.
|
|
|
|
}
|
2018-07-16 09:43:10 -07:00
|
|
|
|
|
|
|
if conf == nil {
|
2018-10-10 15:45:41 -07:00
|
|
|
conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
|
2018-07-16 09:43:10 -07:00
|
|
|
}
|
2018-09-19 08:25:52 -07:00
|
|
|
|
|
|
|
cosmosConfigFilePath := filepath.Join(rootDir, "config/gaiad.toml")
|
|
|
|
viper.SetConfigName("cosmos")
|
|
|
|
_ = viper.MergeInConfig()
|
|
|
|
var cosmosConf *config.Config
|
|
|
|
if _, err := os.Stat(cosmosConfigFilePath); os.IsNotExist(err) {
|
|
|
|
cosmosConf, _ := config.ParseConfig()
|
|
|
|
config.WriteConfigFile(cosmosConfigFilePath, cosmosConf)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cosmosConf == nil {
|
|
|
|
_, err = config.ParseConfig()
|
|
|
|
}
|
|
|
|
|
2018-06-25 09:33:07 -07:00
|
|
|
return
|
|
|
|
}
|
2018-04-05 03:24:53 -07:00
|
|
|
|
2018-09-11 02:15:38 -07:00
|
|
|
// validate the config with the sdk's requirements.
|
|
|
|
func validateConfig(conf *cfg.Config) error {
|
|
|
|
if conf.Consensus.CreateEmptyBlocks == false {
|
|
|
|
return errors.New("config option CreateEmptyBlocks = false is currently unsupported")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-18 21:49:24 -07:00
|
|
|
// add server commands
|
2018-04-05 03:24:53 -07:00
|
|
|
func AddCommands(
|
2018-09-13 11:17:32 -07:00
|
|
|
ctx *Context, cdc *codec.Codec,
|
2018-11-15 10:01:19 -08:00
|
|
|
rootCmd *cobra.Command,
|
2018-04-27 17:36:11 -07:00
|
|
|
appCreator AppCreator, appExport AppExporter) {
|
2018-04-05 03:24:53 -07:00
|
|
|
|
2018-04-21 19:26:46 -07:00
|
|
|
rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level")
|
2018-04-05 04:16:20 -07:00
|
|
|
|
2018-05-31 18:38:15 -07:00
|
|
|
tendermintCmd := &cobra.Command{
|
2018-05-28 15:00:37 -07:00
|
|
|
Use: "tendermint",
|
|
|
|
Short: "Tendermint subcommands",
|
|
|
|
}
|
|
|
|
|
2018-05-31 18:38:15 -07:00
|
|
|
tendermintCmd.AddCommand(
|
2018-04-21 19:26:46 -07:00
|
|
|
ShowNodeIDCmd(ctx),
|
|
|
|
ShowValidatorCmd(ctx),
|
2018-08-30 18:38:28 -07:00
|
|
|
ShowAddressCmd(ctx),
|
2018-05-28 15:00:37 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
rootCmd.AddCommand(
|
|
|
|
StartCmd(ctx, appCreator),
|
2018-06-01 01:35:39 -07:00
|
|
|
UnsafeResetAllCmd(ctx),
|
2018-05-28 15:00:37 -07:00
|
|
|
client.LineBreak,
|
2018-05-31 18:38:15 -07:00
|
|
|
tendermintCmd,
|
2018-04-27 17:36:11 -07:00
|
|
|
ExportCmd(ctx, cdc, appExport),
|
2018-05-28 15:00:37 -07:00
|
|
|
client.LineBreak,
|
2018-04-05 03:24:53 -07:00
|
|
|
version.VersionCmd,
|
|
|
|
)
|
|
|
|
}
|
2018-04-21 19:26:46 -07:00
|
|
|
|
|
|
|
//___________________________________________________________________________________
|
|
|
|
|
2018-07-05 11:07:03 -07:00
|
|
|
// InsertKeyJSON inserts a new JSON field/key with a given value to an existing
|
|
|
|
// JSON message. An error is returned if any serialization operation fails.
|
|
|
|
//
|
|
|
|
// NOTE: The ordering of the keys returned as the resulting JSON message is
|
|
|
|
// non-deterministic, so the client should not rely on key ordering.
|
2018-09-13 11:17:32 -07:00
|
|
|
func InsertKeyJSON(cdc *codec.Codec, baseJSON []byte, key string, value json.RawMessage) ([]byte, error) {
|
2018-04-21 19:26:46 -07:00
|
|
|
var jsonMap map[string]json.RawMessage
|
2018-07-05 11:07:03 -07:00
|
|
|
|
|
|
|
if err := cdc.UnmarshalJSON(baseJSON, &jsonMap); err != nil {
|
2018-04-21 19:26:46 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-05 11:07:03 -07:00
|
|
|
|
2018-04-21 19:26:46 -07:00
|
|
|
jsonMap[key] = value
|
2018-09-13 11:17:32 -07:00
|
|
|
bz, err := codec.MarshalJSONIndent(cdc, jsonMap)
|
2018-07-05 11:07:03 -07:00
|
|
|
|
2018-04-21 19:26:46 -07:00
|
|
|
return json.RawMessage(bz), err
|
|
|
|
}
|
2018-04-23 12:15:50 -07:00
|
|
|
|
|
|
|
// https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
|
|
|
|
// TODO there must be a better way to get external IP
|
2018-10-10 15:45:41 -07:00
|
|
|
func ExternalIP() (string, error) {
|
2018-04-23 12:15:50 -07:00
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
for _, iface := range ifaces {
|
2018-07-07 19:34:46 -07:00
|
|
|
if skipInterface(iface) {
|
|
|
|
continue
|
2018-04-23 12:15:50 -07:00
|
|
|
}
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
for _, addr := range addrs {
|
2018-07-07 19:34:46 -07:00
|
|
|
ip := addrToIP(addr)
|
2018-04-23 12:15:50 -07:00
|
|
|
if ip == nil || ip.IsLoopback() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ip = ip.To4()
|
|
|
|
if ip == nil {
|
|
|
|
continue // not an ipv4 address
|
|
|
|
}
|
|
|
|
return ip.String(), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", errors.New("are you connected to the network?")
|
|
|
|
}
|
2018-07-07 19:34:46 -07:00
|
|
|
|
2018-11-02 06:44:40 -07:00
|
|
|
// TrapSignal traps SIGINT and SIGTERM and terminates the server correctly.
|
|
|
|
func TrapSignal(cleanupFunc func()) {
|
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
|
|
sig := <-sigs
|
|
|
|
switch sig {
|
|
|
|
case syscall.SIGTERM:
|
|
|
|
defer cleanupFunc()
|
|
|
|
os.Exit(128 + int(syscall.SIGTERM))
|
|
|
|
case syscall.SIGINT:
|
|
|
|
defer cleanupFunc()
|
|
|
|
os.Exit(128 + int(syscall.SIGINT))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2018-07-07 19:34:46 -07:00
|
|
|
func skipInterface(iface net.Interface) bool {
|
|
|
|
if iface.Flags&net.FlagUp == 0 {
|
|
|
|
return true // interface down
|
|
|
|
}
|
|
|
|
if iface.Flags&net.FlagLoopback != 0 {
|
|
|
|
return true // loopback interface
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func addrToIP(addr net.Addr) net.IP {
|
|
|
|
var ip net.IP
|
|
|
|
switch v := addr.(type) {
|
|
|
|
case *net.IPNet:
|
|
|
|
ip = v.IP
|
|
|
|
case *net.IPAddr:
|
|
|
|
ip = v.IP
|
|
|
|
}
|
|
|
|
return ip
|
|
|
|
}
|