You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

327 lines
8.6 KiB

  1. package main
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/spf13/viper"
  13. "github.com/tendermint/tendermint/abci/server"
  14. "github.com/tendermint/tendermint/config"
  15. "github.com/tendermint/tendermint/crypto/ed25519"
  16. tmflags "github.com/tendermint/tendermint/libs/cli/flags"
  17. "github.com/tendermint/tendermint/libs/log"
  18. tmnet "github.com/tendermint/tendermint/libs/net"
  19. "github.com/tendermint/tendermint/light"
  20. lproxy "github.com/tendermint/tendermint/light/proxy"
  21. lrpc "github.com/tendermint/tendermint/light/rpc"
  22. dbs "github.com/tendermint/tendermint/light/store/db"
  23. "github.com/tendermint/tendermint/node"
  24. "github.com/tendermint/tendermint/p2p"
  25. "github.com/tendermint/tendermint/privval"
  26. "github.com/tendermint/tendermint/proxy"
  27. rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
  28. e2e "github.com/tendermint/tendermint/test/e2e/pkg"
  29. mcs "github.com/tendermint/tendermint/test/maverick/consensus"
  30. maverick "github.com/tendermint/tendermint/test/maverick/node"
  31. )
  32. var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  33. // main is the binary entrypoint.
  34. func main() {
  35. if len(os.Args) != 2 {
  36. fmt.Printf("Usage: %v <configfile>", os.Args[0])
  37. return
  38. }
  39. configFile := ""
  40. if len(os.Args) == 2 {
  41. configFile = os.Args[1]
  42. }
  43. if err := run(configFile); err != nil {
  44. logger.Error(err.Error())
  45. os.Exit(1)
  46. }
  47. }
  48. // run runs the application - basically like main() with error handling.
  49. func run(configFile string) error {
  50. cfg, err := LoadConfig(configFile)
  51. if err != nil {
  52. return err
  53. }
  54. // Start remote signer (must start before node if running builtin).
  55. if cfg.PrivValServer != "" {
  56. if err = startSigner(cfg); err != nil {
  57. return err
  58. }
  59. if cfg.Protocol == "builtin" {
  60. time.Sleep(1 * time.Second)
  61. }
  62. }
  63. // Start app server.
  64. switch cfg.Protocol {
  65. case "socket", "grpc":
  66. err = startApp(cfg)
  67. case "builtin":
  68. if len(cfg.Misbehaviors) == 0 {
  69. if cfg.Mode == string(e2e.ModeLight) {
  70. err = startLightClient(cfg)
  71. } else {
  72. err = startNode(cfg)
  73. }
  74. } else {
  75. err = startMaverick(cfg)
  76. }
  77. default:
  78. err = fmt.Errorf("invalid protocol %q", cfg.Protocol)
  79. }
  80. if err != nil {
  81. return err
  82. }
  83. // Apparently there's no way to wait for the server, so we just sleep
  84. for {
  85. time.Sleep(1 * time.Hour)
  86. }
  87. }
  88. // startApp starts the application server, listening for connections from Tendermint.
  89. func startApp(cfg *Config) error {
  90. app, err := NewApplication(cfg)
  91. if err != nil {
  92. return err
  93. }
  94. server, err := server.NewServer(cfg.Listen, cfg.Protocol, app)
  95. if err != nil {
  96. return err
  97. }
  98. err = server.Start()
  99. if err != nil {
  100. return err
  101. }
  102. logger.Info(fmt.Sprintf("Server listening on %v (%v protocol)", cfg.Listen, cfg.Protocol))
  103. return nil
  104. }
  105. // startNode starts a Tendermint node running the application directly. It assumes the Tendermint
  106. // configuration is in $TMHOME/config/tendermint.toml.
  107. //
  108. // FIXME There is no way to simply load the configuration from a file, so we need to pull in Viper.
  109. func startNode(cfg *Config) error {
  110. app, err := NewApplication(cfg)
  111. if err != nil {
  112. return err
  113. }
  114. tmcfg, nodeLogger, nodeKey, err := setupNode()
  115. if err != nil {
  116. return fmt.Errorf("failed to setup config: %w", err)
  117. }
  118. n, err := node.NewNode(tmcfg,
  119. privval.LoadOrGenFilePV(tmcfg.PrivValidatorKeyFile(), tmcfg.PrivValidatorStateFile()),
  120. nodeKey,
  121. proxy.NewLocalClientCreator(app),
  122. node.DefaultGenesisDocProviderFunc(tmcfg),
  123. node.DefaultDBProvider,
  124. node.DefaultMetricsProvider(tmcfg.Instrumentation),
  125. nodeLogger,
  126. )
  127. if err != nil {
  128. return err
  129. }
  130. return n.Start()
  131. }
  132. func startLightClient(cfg *Config) error {
  133. tmcfg, nodeLogger, _, err := setupNode()
  134. if err != nil {
  135. return err
  136. }
  137. dbContext := &node.DBContext{ID: "light", Config: tmcfg}
  138. lightDB, err := node.DefaultDBProvider(dbContext)
  139. if err != nil {
  140. return err
  141. }
  142. providers := rpcEndpoints(tmcfg.P2P.PersistentPeers)
  143. c, err := light.NewHTTPClient(
  144. context.Background(),
  145. cfg.ChainID,
  146. light.TrustOptions{
  147. Period: tmcfg.StateSync.TrustPeriod,
  148. Height: tmcfg.StateSync.TrustHeight,
  149. Hash: tmcfg.StateSync.TrustHashBytes(),
  150. },
  151. providers[0],
  152. providers[1:],
  153. dbs.New(lightDB, "light"),
  154. light.Logger(nodeLogger),
  155. )
  156. if err != nil {
  157. return err
  158. }
  159. rpccfg := rpcserver.DefaultConfig()
  160. rpccfg.MaxBodyBytes = tmcfg.RPC.MaxBodyBytes
  161. rpccfg.MaxHeaderBytes = tmcfg.RPC.MaxHeaderBytes
  162. rpccfg.MaxOpenConnections = tmcfg.RPC.MaxOpenConnections
  163. // If necessary adjust global WriteTimeout to ensure it's greater than
  164. // TimeoutBroadcastTxCommit.
  165. // See https://github.com/tendermint/tendermint/issues/3435
  166. if rpccfg.WriteTimeout <= tmcfg.RPC.TimeoutBroadcastTxCommit {
  167. rpccfg.WriteTimeout = tmcfg.RPC.TimeoutBroadcastTxCommit + 1*time.Second
  168. }
  169. p, err := lproxy.NewProxy(c, tmcfg.RPC.ListenAddress, providers[0], rpccfg, nodeLogger,
  170. lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn()))
  171. if err != nil {
  172. return err
  173. }
  174. logger.Info("Starting proxy...", "laddr", tmcfg.RPC.ListenAddress)
  175. if err := p.ListenAndServe(); err != http.ErrServerClosed {
  176. // Error starting or closing listener:
  177. logger.Error("proxy ListenAndServe", "err", err)
  178. }
  179. return nil
  180. }
  181. // FIXME: Temporarily disconnected maverick until it is redesigned
  182. // startMaverick starts a Maverick node that runs the application directly. It assumes the Tendermint
  183. // configuration is in $TMHOME/config/tendermint.toml.
  184. func startMaverick(cfg *Config) error {
  185. app, err := NewApplication(cfg)
  186. if err != nil {
  187. return err
  188. }
  189. tmcfg, logger, nodeKey, err := setupNode()
  190. if err != nil {
  191. return fmt.Errorf("failed to setup config: %w", err)
  192. }
  193. misbehaviors := make(map[int64]mcs.Misbehavior, len(cfg.Misbehaviors))
  194. for heightString, misbehaviorString := range cfg.Misbehaviors {
  195. height, _ := strconv.ParseInt(heightString, 10, 64)
  196. misbehaviors[height] = mcs.MisbehaviorList[misbehaviorString]
  197. }
  198. n, err := maverick.NewNode(tmcfg,
  199. maverick.LoadOrGenFilePV(tmcfg.PrivValidatorKeyFile(), tmcfg.PrivValidatorStateFile()),
  200. nodeKey,
  201. proxy.NewLocalClientCreator(app),
  202. maverick.DefaultGenesisDocProviderFunc(tmcfg),
  203. maverick.DefaultDBProvider,
  204. maverick.DefaultMetricsProvider(tmcfg.Instrumentation),
  205. logger,
  206. misbehaviors,
  207. )
  208. if err != nil {
  209. return err
  210. }
  211. return n.Start()
  212. }
  213. // startSigner starts a signer server connecting to the given endpoint.
  214. func startSigner(cfg *Config) error {
  215. filePV := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
  216. protocol, address := tmnet.ProtocolAndAddress(cfg.PrivValServer)
  217. var dialFn privval.SocketDialer
  218. switch protocol {
  219. case "tcp":
  220. dialFn = privval.DialTCPFn(address, 3*time.Second, ed25519.GenPrivKey())
  221. case "unix":
  222. dialFn = privval.DialUnixFn(address)
  223. default:
  224. return fmt.Errorf("invalid privval protocol %q", protocol)
  225. }
  226. endpoint := privval.NewSignerDialerEndpoint(logger, dialFn,
  227. privval.SignerDialerEndpointRetryWaitInterval(1*time.Second),
  228. privval.SignerDialerEndpointConnRetries(100))
  229. err := privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
  230. if err != nil {
  231. return err
  232. }
  233. logger.Info(fmt.Sprintf("Remote signer connecting to %v", cfg.PrivValServer))
  234. return nil
  235. }
  236. func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) {
  237. var tmcfg *config.Config
  238. home := os.Getenv("TMHOME")
  239. if home == "" {
  240. return nil, nil, nil, errors.New("TMHOME not set")
  241. }
  242. viper.AddConfigPath(filepath.Join(home, "config"))
  243. viper.SetConfigName("config")
  244. if err := viper.ReadInConfig(); err != nil {
  245. return nil, nil, nil, err
  246. }
  247. tmcfg = config.DefaultConfig()
  248. if err := viper.Unmarshal(tmcfg); err != nil {
  249. return nil, nil, nil, err
  250. }
  251. tmcfg.SetRoot(home)
  252. if err := tmcfg.ValidateBasic(); err != nil {
  253. return nil, nil, nil, fmt.Errorf("error in config file: %w", err)
  254. }
  255. if tmcfg.LogFormat == config.LogFormatJSON {
  256. logger = log.NewTMJSONLogger(log.NewSyncWriter(os.Stdout))
  257. }
  258. nodeLogger, err := tmflags.ParseLogLevel(tmcfg.LogLevel, logger, config.DefaultLogLevel)
  259. if err != nil {
  260. return nil, nil, nil, err
  261. }
  262. nodeLogger = nodeLogger.With("module", "main")
  263. nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile())
  264. if err != nil {
  265. return nil, nil, nil, fmt.Errorf("failed to load or gen node key %s: %w", tmcfg.NodeKeyFile(), err)
  266. }
  267. return tmcfg, nodeLogger, nodeKey, nil
  268. }
  269. // rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints
  270. // using 26657 as the port number
  271. func rpcEndpoints(peers string) []string {
  272. arr := strings.Split(peers, ",")
  273. endpoints := make([]string, len(arr))
  274. for i, v := range arr {
  275. urlString := strings.SplitAfter(v, "@")[1]
  276. hostName := strings.Split(urlString, ":26656")[0]
  277. // use RPC port instead
  278. port := 26657
  279. rpcEndpoint := "http://" + hostName + ":" + fmt.Sprint(port)
  280. endpoints[i] = rpcEndpoint
  281. }
  282. return endpoints
  283. }