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.

342 lines
8.6 KiB

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