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.

320 lines
7.9 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. var logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false)
  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. switch cfg.Mode {
  70. case string(e2e.ModeLight):
  71. err = startLightNode(cfg)
  72. case string(e2e.ModeSeed):
  73. err = startSeedNode()
  74. default:
  75. err = startNode(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 := app.NewApplication(cfg.App())
  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 := app.NewApplication(cfg.App())
  111. if err != nil {
  112. return err
  113. }
  114. tmcfg, nodeLogger, err := setupNode()
  115. if err != nil {
  116. return fmt.Errorf("failed to setup config: %w", err)
  117. }
  118. n, err := node.New(tmcfg,
  119. nodeLogger,
  120. abciclient.NewLocalCreator(app),
  121. nil,
  122. )
  123. if err != nil {
  124. return err
  125. }
  126. return n.Start()
  127. }
  128. func startSeedNode() error {
  129. tmcfg, nodeLogger, err := setupNode()
  130. if err != nil {
  131. return fmt.Errorf("failed to setup config: %w", err)
  132. }
  133. tmcfg.Mode = config.ModeSeed
  134. n, err := node.New(tmcfg, nodeLogger, nil, nil)
  135. if err != nil {
  136. return err
  137. }
  138. return n.Start()
  139. }
  140. func startLightNode(cfg *Config) error {
  141. tmcfg, nodeLogger, err := setupNode()
  142. if err != nil {
  143. return err
  144. }
  145. dbContext := &config.DBContext{ID: "light", Config: tmcfg}
  146. lightDB, err := config.DefaultDBProvider(dbContext)
  147. if err != nil {
  148. return err
  149. }
  150. providers := rpcEndpoints(tmcfg.P2P.PersistentPeers)
  151. c, err := light.NewHTTPClient(
  152. context.Background(),
  153. cfg.ChainID,
  154. light.TrustOptions{
  155. Period: tmcfg.StateSync.TrustPeriod,
  156. Height: tmcfg.StateSync.TrustHeight,
  157. Hash: tmcfg.StateSync.TrustHashBytes(),
  158. },
  159. providers[0],
  160. providers[1:],
  161. dbs.New(lightDB),
  162. light.Logger(nodeLogger),
  163. )
  164. if err != nil {
  165. return err
  166. }
  167. rpccfg := rpcserver.DefaultConfig()
  168. rpccfg.MaxBodyBytes = tmcfg.RPC.MaxBodyBytes
  169. rpccfg.MaxHeaderBytes = tmcfg.RPC.MaxHeaderBytes
  170. rpccfg.MaxOpenConnections = tmcfg.RPC.MaxOpenConnections
  171. // If necessary adjust global WriteTimeout to ensure it's greater than
  172. // TimeoutBroadcastTxCommit.
  173. // See https://github.com/tendermint/tendermint/issues/3435
  174. if rpccfg.WriteTimeout <= tmcfg.RPC.TimeoutBroadcastTxCommit {
  175. rpccfg.WriteTimeout = tmcfg.RPC.TimeoutBroadcastTxCommit + 1*time.Second
  176. }
  177. p, err := lproxy.NewProxy(c, tmcfg.RPC.ListenAddress, providers[0], rpccfg, nodeLogger,
  178. lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn()))
  179. if err != nil {
  180. return err
  181. }
  182. logger.Info("Starting proxy...", "laddr", tmcfg.RPC.ListenAddress)
  183. if err := p.ListenAndServe(); err != http.ErrServerClosed {
  184. // Error starting or closing listener:
  185. logger.Error("proxy ListenAndServe", "err", err)
  186. }
  187. return nil
  188. }
  189. // startSigner starts a signer server connecting to the given endpoint.
  190. func startSigner(cfg *Config) error {
  191. filePV, err := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
  192. if err != nil {
  193. return err
  194. }
  195. protocol, address := tmnet.ProtocolAndAddress(cfg.PrivValServer)
  196. var dialFn privval.SocketDialer
  197. switch protocol {
  198. case "tcp":
  199. dialFn = privval.DialTCPFn(address, 3*time.Second, ed25519.GenPrivKey())
  200. case "unix":
  201. dialFn = privval.DialUnixFn(address)
  202. case "grpc":
  203. lis, err := net.Listen("tcp", address)
  204. if err != nil {
  205. return err
  206. }
  207. ss := grpcprivval.NewSignerServer(cfg.ChainID, filePV, logger)
  208. s := grpc.NewServer()
  209. privvalproto.RegisterPrivValidatorAPIServer(s, ss)
  210. go func() { // no need to clean up since we remove docker containers
  211. if err := s.Serve(lis); err != nil {
  212. panic(err)
  213. }
  214. }()
  215. return nil
  216. default:
  217. return fmt.Errorf("invalid privval protocol %q", protocol)
  218. }
  219. endpoint := privval.NewSignerDialerEndpoint(logger, dialFn,
  220. privval.SignerDialerEndpointRetryWaitInterval(1*time.Second),
  221. privval.SignerDialerEndpointConnRetries(100))
  222. err = privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
  223. if err != nil {
  224. return err
  225. }
  226. logger.Info(fmt.Sprintf("Remote signer connecting to %v", cfg.PrivValServer))
  227. return nil
  228. }
  229. func setupNode() (*config.Config, log.Logger, error) {
  230. var tmcfg *config.Config
  231. home := os.Getenv("TMHOME")
  232. if home == "" {
  233. return nil, nil, errors.New("TMHOME not set")
  234. }
  235. viper.AddConfigPath(filepath.Join(home, "config"))
  236. viper.SetConfigName("config")
  237. if err := viper.ReadInConfig(); err != nil {
  238. return nil, nil, err
  239. }
  240. tmcfg = config.DefaultConfig()
  241. if err := viper.Unmarshal(tmcfg); err != nil {
  242. return nil, nil, err
  243. }
  244. tmcfg.SetRoot(home)
  245. if err := tmcfg.ValidateBasic(); err != nil {
  246. return nil, nil, fmt.Errorf("error in config file: %w", err)
  247. }
  248. nodeLogger, err := log.NewDefaultLogger(tmcfg.LogFormat, tmcfg.LogLevel, false)
  249. if err != nil {
  250. return nil, nil, err
  251. }
  252. return tmcfg, nodeLogger.With("module", "main"), nil
  253. }
  254. // rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints
  255. // using 26657 as the port number
  256. func rpcEndpoints(peers string) []string {
  257. arr := strings.Split(peers, ",")
  258. endpoints := make([]string, len(arr))
  259. for i, v := range arr {
  260. addr, err := p2p.ParseNodeAddress(v)
  261. if err != nil {
  262. panic(err)
  263. }
  264. // use RPC port instead
  265. addr.Port = 26657
  266. var rpcEndpoint string
  267. // for ipv6 addresses
  268. if strings.Contains(addr.Hostname, ":") {
  269. rpcEndpoint = "http://[" + addr.Hostname + "]:" + fmt.Sprint(addr.Port)
  270. } else { // for ipv4 addresses
  271. rpcEndpoint = "http://" + addr.Hostname + ":" + fmt.Sprint(addr.Port)
  272. }
  273. endpoints[i] = rpcEndpoint
  274. }
  275. return endpoints
  276. }