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.

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