github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/server/util.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"net"
     7  	"os"
     8  	"os/signal"
     9  	"path/filepath"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags"
    14  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/lcd"
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/server/config"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/version"
    18  	tcmd "github.com/fibonacci-chain/fbc/libs/tendermint/cmd/tendermint/commands"
    19  	cfg "github.com/fibonacci-chain/fbc/libs/tendermint/config"
    20  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/cli"
    21  	tmflags "github.com/fibonacci-chain/fbc/libs/tendermint/libs/cli/flags"
    22  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    23  	"github.com/fibonacci-chain/fbc/libs/tendermint/state"
    24  	"github.com/gogo/protobuf/jsonpb"
    25  	"github.com/google/gops/agent"
    26  	"github.com/spf13/cobra"
    27  	"github.com/spf13/viper"
    28  )
    29  
    30  const FlagGops = "gops"
    31  
    32  // server context
    33  type Context struct {
    34  	Config *cfg.Config
    35  	Logger log.Logger
    36  }
    37  
    38  func NewDefaultContext() *Context {
    39  	return NewContext(
    40  		cfg.DefaultConfig(),
    41  		log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
    42  	)
    43  }
    44  
    45  func NewContext(config *cfg.Config, logger log.Logger) *Context {
    46  	return &Context{config, logger}
    47  }
    48  
    49  //___________________________________________________________________________________
    50  
    51  // PersistentPreRunEFn returns a PersistentPreRunE function for cobra
    52  // that initailizes the passed in context with a properly configured
    53  // logger and config object.
    54  func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error {
    55  	return func(cmd *cobra.Command, args []string) error {
    56  		if cmd.Name() == version.Cmd.Name() {
    57  			return nil
    58  		}
    59  		config, err := interceptLoadConfig()
    60  		if err != nil {
    61  			return err
    62  		}
    63  		if !viper.IsSet(state.FlagDeliverTxsExecMode) {
    64  			if viper.GetBool(state.FlagEnableConcurrency) {
    65  				viper.Set(state.FlagDeliverTxsExecMode, state.DeliverTxsExecModeParallel)
    66  			}
    67  		}
    68  		// okchain
    69  		output := os.Stdout
    70  		if !config.LogStdout {
    71  			output, err = os.OpenFile(config.LogFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
    72  			if err != nil {
    73  				return err
    74  			}
    75  		}
    76  
    77  		logger := log.NewTMLogger(log.NewSyncWriter(output))
    78  		logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
    79  		if err != nil {
    80  			return err
    81  		}
    82  		if viper.GetBool(cli.TraceFlag) {
    83  			logger = log.NewTracingLogger(logger)
    84  		}
    85  		logger = logger.With("module", "main")
    86  		context.Config = config
    87  		context.Logger = logger
    88  
    89  		if viper.GetBool(FlagGops) {
    90  			err = agent.Listen(agent.Options{ShutdownCleanup: true})
    91  			if err != nil {
    92  				logger.Error("gops agent error", "err", err)
    93  			}
    94  		}
    95  
    96  		return nil
    97  	}
    98  }
    99  
   100  // If a new config is created, change some of the default tendermint settings
   101  func interceptLoadConfig() (conf *cfg.Config, err error) {
   102  	tmpConf := cfg.DefaultConfig()
   103  	err = viper.Unmarshal(tmpConf)
   104  	if err != nil {
   105  		// TODO: Handle with #870
   106  		panic(err)
   107  	}
   108  	rootDir := tmpConf.RootDir
   109  	configFilePath := filepath.Join(rootDir, "config/config.toml")
   110  	// Intercept only if the file doesn't already exist
   111  
   112  	if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
   113  		// the following parse config is needed to create directories
   114  		conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
   115  		conf.ProfListenAddress = "localhost:6060"
   116  		conf.P2P.RecvRate = 5120000
   117  		conf.P2P.SendRate = 5120000
   118  		conf.TxIndex.IndexAllKeys = true
   119  		conf.Consensus.TimeoutCommit = 3 * time.Second
   120  		conf.Consensus.TimeoutConsensus = 1 * time.Second
   121  		cfg.WriteConfigFile(configFilePath, conf)
   122  		// Fall through, just so that its parsed into memory.
   123  	}
   124  
   125  	if conf == nil {
   126  		conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary.
   127  		if err != nil {
   128  			panic(err)
   129  		}
   130  	}
   131  
   132  	appConfigFilePath := filepath.Join(rootDir, "config/fbchaind.toml")
   133  	if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) {
   134  		appConf, _ := config.ParseConfig()
   135  		config.WriteConfigFile(appConfigFilePath, appConf)
   136  	}
   137  
   138  	viper.SetConfigName("fbchaind")
   139  	err = viper.MergeInConfig()
   140  
   141  	return conf, err
   142  }
   143  
   144  // add server commands
   145  func AddCommands(
   146  	ctx *Context, cdc *codec.CodecProxy,
   147  	registry jsonpb.AnyResolver,
   148  	rootCmd *cobra.Command,
   149  	appCreator AppCreator, appStop AppStop, appExport AppExporter,
   150  	registerRouters func(rs *lcd.RestServer),
   151  	registerAppFlagFn func(cmd *cobra.Command),
   152  	appPreRun func(ctx *Context, cmd *cobra.Command) error,
   153  	subFunc func(logger log.Logger) log.Subscriber) {
   154  
   155  	rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level")
   156  	rootCmd.PersistentFlags().String("log_file", ctx.Config.LogFile, "Log file")
   157  	rootCmd.PersistentFlags().Bool("log_stdout", ctx.Config.LogStdout, "Print log to stdout, rather than a file")
   158  
   159  	tendermintCmd := &cobra.Command{
   160  		Use:   "tendermint",
   161  		Short: "Tendermint subcommands",
   162  	}
   163  
   164  	tendermintCmd.AddCommand(
   165  		ShowNodeIDCmd(ctx),
   166  		ShowValidatorCmd(ctx),
   167  		ShowAddressCmd(ctx),
   168  		VersionCmd(ctx),
   169  	)
   170  
   171  	rootCmd.AddCommand(
   172  		StartCmd(ctx, cdc, registry, appCreator, appStop, registerRouters, registerAppFlagFn, appPreRun, subFunc),
   173  		StopCmd(ctx),
   174  		UnsafeResetAllCmd(ctx),
   175  		flags.LineBreak,
   176  		tendermintCmd,
   177  		ExportCmd(ctx, cdc.GetCdc(), appExport),
   178  		flags.LineBreak,
   179  		version.Cmd,
   180  	)
   181  }
   182  
   183  //___________________________________________________________________________________
   184  
   185  // InsertKeyJSON inserts a new JSON field/key with a given value to an existing
   186  // JSON message. An error is returned if any serialization operation fails.
   187  //
   188  // NOTE: The ordering of the keys returned as the resulting JSON message is
   189  // non-deterministic, so the client should not rely on key ordering.
   190  func InsertKeyJSON(cdc *codec.Codec, baseJSON []byte, key string, value json.RawMessage) ([]byte, error) {
   191  	var jsonMap map[string]json.RawMessage
   192  
   193  	if err := cdc.UnmarshalJSON(baseJSON, &jsonMap); err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	jsonMap[key] = value
   198  	bz, err := codec.MarshalJSONIndent(cdc, jsonMap)
   199  
   200  	return json.RawMessage(bz), err
   201  }
   202  
   203  // https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
   204  // TODO there must be a better way to get external IP
   205  func ExternalIP() (string, error) {
   206  	ifaces, err := net.Interfaces()
   207  	if err != nil {
   208  		return "", err
   209  	}
   210  	for _, iface := range ifaces {
   211  		if skipInterface(iface) {
   212  			continue
   213  		}
   214  		addrs, err := iface.Addrs()
   215  		if err != nil {
   216  			return "", err
   217  		}
   218  		for _, addr := range addrs {
   219  			ip := addrToIP(addr)
   220  			if ip == nil || ip.IsLoopback() {
   221  				continue
   222  			}
   223  			ip = ip.To4()
   224  			if ip == nil {
   225  				continue // not an ipv4 address
   226  			}
   227  			return ip.String(), nil
   228  		}
   229  	}
   230  	return "", errors.New("are you connected to the network?")
   231  }
   232  
   233  // TrapSignal traps SIGINT and SIGTERM and terminates the server correctly.
   234  func TrapSignal(cleanupFunc func()) {
   235  	sigs := make(chan os.Signal, 1)
   236  	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
   237  	go func() {
   238  		sig := <-sigs
   239  		if cleanupFunc != nil {
   240  			cleanupFunc()
   241  		}
   242  		exitCode := 128
   243  		switch sig {
   244  		case syscall.SIGINT:
   245  			exitCode += int(syscall.SIGINT)
   246  		case syscall.SIGTERM:
   247  			exitCode += int(syscall.SIGTERM)
   248  		}
   249  		os.Exit(exitCode)
   250  	}()
   251  }
   252  
   253  func skipInterface(iface net.Interface) bool {
   254  	if iface.Flags&net.FlagUp == 0 {
   255  		return true // interface down
   256  	}
   257  	if iface.Flags&net.FlagLoopback != 0 {
   258  		return true // loopback interface
   259  	}
   260  	return false
   261  }
   262  
   263  func addrToIP(addr net.Addr) net.IP {
   264  	var ip net.IP
   265  	switch v := addr.(type) {
   266  	case *net.IPNet:
   267  		ip = v.IP
   268  	case *net.IPAddr:
   269  		ip = v.IP
   270  	}
   271  	return ip
   272  }
   273  
   274  // DONTCOVER