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.

1453 lines
45 KiB

p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
p2p: implement new Transport interface (#5791) This implements a new `Transport` interface and related types for the P2P refactor in #5670. Previously, `conn.MConnection` was very tightly coupled to the `Peer` implementation -- in order to allow alternative non-multiplexed transports (e.g. QUIC), MConnection has now been moved below the `Transport` interface, as `MConnTransport`, and decoupled from the peer. Since the `p2p` package is not covered by our Go API stability, this is not considered a breaking change, and not listed in the changelog. The initial approach was to implement the new interface in its final form (which also involved possible protocol changes, see https://github.com/tendermint/spec/pull/227). However, it turned out that this would require a large amount of changes to existing P2P code because of the previous tight coupling between `Peer` and `MConnection` and the reliance on subtleties in the MConnection behavior. Instead, I have broadened the `Transport` interface to expose much of the existing MConnection interface, preserved much of the existing MConnection logic and behavior in the transport implementation, and tried to make as few changes to the rest of the P2P stack as possible. We will instead reduce this interface gradually as we refactor other parts of the P2P stack. The low-level transport code and protocol (e.g. MConnection, SecretConnection and so on) has not been significantly changed, and refactoring this is not a priority until we come up with a plan for QUIC adoption, as we may end up discarding the MConnection code entirely. There are no tests of the new `MConnTransport`, as this code is likely to evolve as we proceed with the P2P refactor, but tests should be added before a final release. The E2E tests are sufficient for basic validation in the meanwhile.
4 years ago
  1. package node
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. _ "net/http/pprof" // nolint: gosec // securely exposed on separate, optional port
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/prometheus/client_golang/prometheus"
  14. "github.com/prometheus/client_golang/prometheus/promhttp"
  15. "github.com/rs/cors"
  16. dbm "github.com/tendermint/tm-db"
  17. abci "github.com/tendermint/tendermint/abci/types"
  18. bcv0 "github.com/tendermint/tendermint/blockchain/v0"
  19. bcv2 "github.com/tendermint/tendermint/blockchain/v2"
  20. cfg "github.com/tendermint/tendermint/config"
  21. "github.com/tendermint/tendermint/consensus"
  22. "github.com/tendermint/tendermint/crypto"
  23. "github.com/tendermint/tendermint/evidence"
  24. tmjson "github.com/tendermint/tendermint/libs/json"
  25. "github.com/tendermint/tendermint/libs/log"
  26. tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
  27. "github.com/tendermint/tendermint/libs/service"
  28. "github.com/tendermint/tendermint/light"
  29. mempl "github.com/tendermint/tendermint/mempool"
  30. "github.com/tendermint/tendermint/p2p"
  31. "github.com/tendermint/tendermint/p2p/pex"
  32. "github.com/tendermint/tendermint/privval"
  33. "github.com/tendermint/tendermint/proxy"
  34. rpccore "github.com/tendermint/tendermint/rpc/core"
  35. grpccore "github.com/tendermint/tendermint/rpc/grpc"
  36. rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
  37. sm "github.com/tendermint/tendermint/state"
  38. "github.com/tendermint/tendermint/state/txindex"
  39. "github.com/tendermint/tendermint/state/txindex/kv"
  40. "github.com/tendermint/tendermint/state/txindex/null"
  41. "github.com/tendermint/tendermint/statesync"
  42. "github.com/tendermint/tendermint/store"
  43. cs "github.com/tendermint/tendermint/test/maverick/consensus"
  44. "github.com/tendermint/tendermint/types"
  45. tmtime "github.com/tendermint/tendermint/types/time"
  46. "github.com/tendermint/tendermint/version"
  47. )
  48. //------------------------------------------------------------------------------
  49. // ParseMisbehaviors is a util function that converts a comma separated string into
  50. // a map of misbehaviors to be executed by the maverick node
  51. func ParseMisbehaviors(str string) (map[int64]cs.Misbehavior, error) {
  52. // check if string is empty in which case we run a normal node
  53. var misbehaviors = make(map[int64]cs.Misbehavior)
  54. if str == "" {
  55. return misbehaviors, nil
  56. }
  57. strs := strings.Split(str, ",")
  58. if len(strs)%2 != 0 {
  59. return misbehaviors, errors.New("missing either height or misbehavior name in the misbehavior flag")
  60. }
  61. OUTER_LOOP:
  62. for i := 0; i < len(strs); i += 2 {
  63. height, err := strconv.ParseInt(strs[i+1], 10, 64)
  64. if err != nil {
  65. return misbehaviors, fmt.Errorf("failed to parse misbehavior height: %w", err)
  66. }
  67. for key, misbehavior := range cs.MisbehaviorList {
  68. if key == strs[i] {
  69. misbehaviors[height] = misbehavior
  70. continue OUTER_LOOP
  71. }
  72. }
  73. return misbehaviors, fmt.Errorf("received unknown misbehavior: %s. Did you forget to add it?", strs[i])
  74. }
  75. return misbehaviors, nil
  76. }
  77. // DBContext specifies config information for loading a new DB.
  78. type DBContext struct {
  79. ID string
  80. Config *cfg.Config
  81. }
  82. // DBProvider takes a DBContext and returns an instantiated DB.
  83. type DBProvider func(*DBContext) (dbm.DB, error)
  84. // DefaultDBProvider returns a database using the DBBackend and DBDir
  85. // specified in the ctx.Config.
  86. func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) {
  87. dbType := dbm.BackendType(ctx.Config.DBBackend)
  88. return dbm.NewDB(ctx.ID, dbType, ctx.Config.DBDir())
  89. }
  90. // GenesisDocProvider returns a GenesisDoc.
  91. // It allows the GenesisDoc to be pulled from sources other than the
  92. // filesystem, for instance from a distributed key-value store cluster.
  93. type GenesisDocProvider func() (*types.GenesisDoc, error)
  94. // DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads
  95. // the GenesisDoc from the config.GenesisFile() on the filesystem.
  96. func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider {
  97. return func() (*types.GenesisDoc, error) {
  98. return types.GenesisDocFromFile(config.GenesisFile())
  99. }
  100. }
  101. // Provider takes a config and a logger and returns a ready to go Node.
  102. type Provider func(*cfg.Config, log.Logger) (*Node, error)
  103. // DefaultNewNode returns a Tendermint node with default settings for the
  104. // PrivValidator, ClientCreator, GenesisDoc, and DBProvider.
  105. // It implements NodeProvider.
  106. func DefaultNewNode(config *cfg.Config, logger log.Logger, misbehaviors map[int64]cs.Misbehavior) (*Node, error) {
  107. nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
  108. if err != nil {
  109. return nil, fmt.Errorf("failed to load or gen node key %s, err: %w", config.NodeKeyFile(), err)
  110. }
  111. return NewNode(config,
  112. LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()),
  113. nodeKey,
  114. proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
  115. DefaultGenesisDocProviderFunc(config),
  116. DefaultDBProvider,
  117. DefaultMetricsProvider(config.Instrumentation),
  118. logger,
  119. misbehaviors,
  120. )
  121. }
  122. // MetricsProvider returns a consensus, p2p and mempool Metrics.
  123. type MetricsProvider func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics)
  124. // DefaultMetricsProvider returns Metrics build using Prometheus client library
  125. // if Prometheus is enabled. Otherwise, it returns no-op Metrics.
  126. func DefaultMetricsProvider(config *cfg.InstrumentationConfig) MetricsProvider {
  127. return func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics) {
  128. if config.Prometheus {
  129. return cs.PrometheusMetrics(config.Namespace, "chain_id", chainID),
  130. p2p.PrometheusMetrics(config.Namespace, "chain_id", chainID),
  131. mempl.PrometheusMetrics(config.Namespace, "chain_id", chainID),
  132. sm.PrometheusMetrics(config.Namespace, "chain_id", chainID)
  133. }
  134. return cs.NopMetrics(), p2p.NopMetrics(), mempl.NopMetrics(), sm.NopMetrics()
  135. }
  136. }
  137. // Option sets a parameter for the node.
  138. type Option func(*Node)
  139. // Temporary interface for switching to fast sync, we should get rid of v0.
  140. // See: https://github.com/tendermint/tendermint/issues/4595
  141. type fastSyncReactor interface {
  142. SwitchToFastSync(sm.State) error
  143. }
  144. // CustomReactors allows you to add custom reactors (name -> p2p.Reactor) to
  145. // the node's Switch.
  146. //
  147. // WARNING: using any name from the below list of the existing reactors will
  148. // result in replacing it with the custom one.
  149. //
  150. // - MEMPOOL
  151. // - BLOCKCHAIN
  152. // - CONSENSUS
  153. // - EVIDENCE
  154. // - PEX
  155. // - STATESYNC
  156. func CustomReactors(reactors map[string]p2p.Reactor) Option {
  157. return func(n *Node) {
  158. for name, reactor := range reactors {
  159. if existingReactor := n.sw.Reactor(name); existingReactor != nil {
  160. n.sw.Logger.Info("Replacing existing reactor with a custom one",
  161. "name", name, "existing", existingReactor, "custom", reactor)
  162. n.sw.RemoveReactor(name, existingReactor)
  163. }
  164. n.sw.AddReactor(name, reactor)
  165. }
  166. }
  167. }
  168. func CustomReactorsAsConstructors(reactors map[string]func(n *Node) p2p.Reactor) Option {
  169. return func(n *Node) {
  170. for name, customReactor := range reactors {
  171. if existingReactor := n.sw.Reactor(name); existingReactor != nil {
  172. n.sw.Logger.Info("Replacing existing reactor with a custom one",
  173. "name", name)
  174. n.sw.RemoveReactor(name, existingReactor)
  175. }
  176. n.sw.AddReactor(name, customReactor(n))
  177. }
  178. }
  179. }
  180. // StateProvider overrides the state provider used by state sync to retrieve trusted app hashes and
  181. // build a State object for bootstrapping the node.
  182. // WARNING: this interface is considered unstable and subject to change.
  183. func StateProvider(stateProvider statesync.StateProvider) Option {
  184. return func(n *Node) {
  185. n.stateSyncProvider = stateProvider
  186. }
  187. }
  188. //------------------------------------------------------------------------------
  189. // Node is the highest level interface to a full Tendermint node.
  190. // It includes all configuration information and running services.
  191. type Node struct {
  192. service.BaseService
  193. // config
  194. config *cfg.Config
  195. genesisDoc *types.GenesisDoc // initial validator set
  196. privValidator types.PrivValidator // local node's validator key
  197. // network
  198. transport *p2p.MConnTransport
  199. sw *p2p.Switch // p2p connections
  200. addrBook pex.AddrBook // known peers
  201. nodeInfo p2p.NodeInfo
  202. nodeKey p2p.NodeKey // our node privkey
  203. isListening bool
  204. // services
  205. eventBus *types.EventBus // pub/sub for services
  206. stateStore sm.Store
  207. blockStore *store.BlockStore // store the blockchain to disk
  208. bcReactor p2p.Reactor // for fast-syncing
  209. mempoolReactor *mempl.Reactor // for gossipping transactions
  210. mempool mempl.Mempool
  211. stateSync bool // whether the node should state sync on startup
  212. stateSyncReactor *statesync.Reactor // for hosting and restoring state sync snapshots
  213. stateSyncProvider statesync.StateProvider // provides state data for bootstrapping a node
  214. stateSyncGenesis sm.State // provides the genesis state for state sync
  215. consensusState *cs.State // latest consensus state
  216. consensusReactor *cs.Reactor // for participating in the consensus
  217. pexReactor *pex.Reactor // for exchanging peer addresses
  218. evidencePool *evidence.Pool // tracking evidence
  219. proxyApp proxy.AppConns // connection to the application
  220. rpcListeners []net.Listener // rpc servers
  221. txIndexer txindex.TxIndexer
  222. indexerService *txindex.IndexerService
  223. prometheusSrv *http.Server
  224. }
  225. func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) {
  226. var blockStoreDB dbm.DB
  227. blockStoreDB, err = dbProvider(&DBContext{"blockstore", config})
  228. if err != nil {
  229. return
  230. }
  231. blockStore = store.NewBlockStore(blockStoreDB)
  232. stateDB, err = dbProvider(&DBContext{"state", config})
  233. if err != nil {
  234. return
  235. }
  236. return
  237. }
  238. func createAndStartProxyAppConns(clientCreator proxy.ClientCreator, logger log.Logger) (proxy.AppConns, error) {
  239. proxyApp := proxy.NewAppConns(clientCreator)
  240. proxyApp.SetLogger(logger.With("module", "proxy"))
  241. if err := proxyApp.Start(); err != nil {
  242. return nil, fmt.Errorf("error starting proxy app connections: %v", err)
  243. }
  244. return proxyApp, nil
  245. }
  246. func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) {
  247. eventBus := types.NewEventBus()
  248. eventBus.SetLogger(logger.With("module", "events"))
  249. if err := eventBus.Start(); err != nil {
  250. return nil, err
  251. }
  252. return eventBus, nil
  253. }
  254. func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider,
  255. eventBus *types.EventBus, logger log.Logger) (*txindex.IndexerService, txindex.TxIndexer, error) {
  256. var txIndexer txindex.TxIndexer
  257. switch config.TxIndex.Indexer {
  258. case "kv":
  259. store, err := dbProvider(&DBContext{"tx_index", config})
  260. if err != nil {
  261. return nil, nil, err
  262. }
  263. txIndexer = kv.NewTxIndex(store)
  264. default:
  265. txIndexer = &null.TxIndex{}
  266. }
  267. indexerService := txindex.NewIndexerService(txIndexer, eventBus)
  268. indexerService.SetLogger(logger.With("module", "txindex"))
  269. if err := indexerService.Start(); err != nil {
  270. return nil, nil, err
  271. }
  272. return indexerService, txIndexer, nil
  273. }
  274. func doHandshake(
  275. stateStore sm.Store,
  276. state sm.State,
  277. blockStore sm.BlockStore,
  278. genDoc *types.GenesisDoc,
  279. eventBus types.BlockEventPublisher,
  280. proxyApp proxy.AppConns,
  281. consensusLogger log.Logger) error {
  282. handshaker := cs.NewHandshaker(stateStore, state, blockStore, genDoc)
  283. handshaker.SetLogger(consensusLogger)
  284. handshaker.SetEventBus(eventBus)
  285. if err := handshaker.Handshake(proxyApp); err != nil {
  286. return fmt.Errorf("error during handshake: %v", err)
  287. }
  288. return nil
  289. }
  290. func logNodeStartupInfo(state sm.State, pubKey crypto.PubKey, logger, consensusLogger log.Logger) {
  291. // Log the version info.
  292. logger.Info("Version info",
  293. "software", version.TMCoreSemVer,
  294. "block", version.BlockProtocol,
  295. "p2p", version.P2PProtocol,
  296. )
  297. // If the state and software differ in block version, at least log it.
  298. if state.Version.Consensus.Block != version.BlockProtocol {
  299. logger.Info("Software and state have different block protocols",
  300. "software", version.BlockProtocol,
  301. "state", state.Version.Consensus.Block,
  302. )
  303. }
  304. addr := pubKey.Address()
  305. // Log whether this node is a validator or an observer
  306. if state.Validators.HasAddress(addr) {
  307. consensusLogger.Info("This node is a validator", "addr", addr, "pubKey", pubKey)
  308. } else {
  309. consensusLogger.Info("This node is not a validator", "addr", addr, "pubKey", pubKey)
  310. }
  311. }
  312. func onlyValidatorIsUs(state sm.State, pubKey crypto.PubKey) bool {
  313. if state.Validators.Size() > 1 {
  314. return false
  315. }
  316. addr, _ := state.Validators.GetByIndex(0)
  317. return bytes.Equal(pubKey.Address(), addr)
  318. }
  319. func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns,
  320. state sm.State, memplMetrics *mempl.Metrics, logger log.Logger) (*mempl.Reactor, *mempl.CListMempool) {
  321. mempool := mempl.NewCListMempool(
  322. config.Mempool,
  323. proxyApp.Mempool(),
  324. state.LastBlockHeight,
  325. mempl.WithMetrics(memplMetrics),
  326. mempl.WithPreCheck(sm.TxPreCheck(state)),
  327. mempl.WithPostCheck(sm.TxPostCheck(state)),
  328. )
  329. mempoolLogger := logger.With("module", "mempool")
  330. mempoolReactor := mempl.NewReactor(config.Mempool, mempool)
  331. mempoolReactor.SetLogger(mempoolLogger)
  332. if config.Consensus.WaitForTxs() {
  333. mempool.EnableTxsAvailable()
  334. }
  335. return mempoolReactor, mempool
  336. }
  337. func createEvidenceReactor(config *cfg.Config, dbProvider DBProvider,
  338. stateDB dbm.DB, blockStore *store.BlockStore, logger log.Logger) (*evidence.Reactor, *evidence.Pool, error) {
  339. evidenceDB, err := dbProvider(&DBContext{"evidence", config})
  340. if err != nil {
  341. return nil, nil, err
  342. }
  343. evidenceLogger := logger.With("module", "evidence")
  344. evidencePool, err := evidence.NewPool(evidenceDB, sm.NewStore(stateDB), blockStore)
  345. if err != nil {
  346. return nil, nil, err
  347. }
  348. evidenceReactor := evidence.NewReactor(evidencePool)
  349. evidenceReactor.SetLogger(evidenceLogger)
  350. return evidenceReactor, evidencePool, nil
  351. }
  352. func createBlockchainReactor(config *cfg.Config,
  353. state sm.State,
  354. blockExec *sm.BlockExecutor,
  355. blockStore *store.BlockStore,
  356. fastSync bool,
  357. logger log.Logger) (bcReactor p2p.Reactor, err error) {
  358. switch config.FastSync.Version {
  359. case "v0":
  360. bcReactor = bcv0.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
  361. case "v2":
  362. bcReactor = bcv2.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
  363. default:
  364. return nil, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version)
  365. }
  366. bcReactor.SetLogger(logger.With("module", "blockchain"))
  367. return bcReactor, nil
  368. }
  369. func createConsensusReactor(config *cfg.Config,
  370. state sm.State,
  371. blockExec *sm.BlockExecutor,
  372. blockStore sm.BlockStore,
  373. mempool *mempl.CListMempool,
  374. evidencePool *evidence.Pool,
  375. privValidator types.PrivValidator,
  376. csMetrics *cs.Metrics,
  377. waitSync bool,
  378. eventBus *types.EventBus,
  379. consensusLogger log.Logger,
  380. misbehaviors map[int64]cs.Misbehavior) (*cs.Reactor, *cs.State) {
  381. consensusState := cs.NewState(
  382. config.Consensus,
  383. state.Copy(),
  384. blockExec,
  385. blockStore,
  386. mempool,
  387. evidencePool,
  388. misbehaviors,
  389. cs.StateMetrics(csMetrics),
  390. )
  391. consensusState.SetLogger(consensusLogger)
  392. if privValidator != nil {
  393. consensusState.SetPrivValidator(privValidator)
  394. }
  395. consensusReactor := cs.NewReactor(consensusState, waitSync, cs.ReactorMetrics(csMetrics))
  396. consensusReactor.SetLogger(consensusLogger)
  397. // services which will be publishing and/or subscribing for messages (events)
  398. // consensusReactor will set it on consensusState and blockExecutor
  399. consensusReactor.SetEventBus(eventBus)
  400. return consensusReactor, consensusState
  401. }
  402. func createTransport(
  403. logger log.Logger,
  404. config *cfg.Config,
  405. nodeInfo p2p.NodeInfo,
  406. nodeKey p2p.NodeKey,
  407. proxyApp proxy.AppConns,
  408. ) (
  409. *p2p.MConnTransport,
  410. []p2p.PeerFilterFunc,
  411. ) {
  412. var (
  413. connFilters = []p2p.ConnFilterFunc{}
  414. peerFilters = []p2p.PeerFilterFunc{}
  415. )
  416. if !config.P2P.AllowDuplicateIP {
  417. connFilters = append(connFilters, p2p.ConnDuplicateIPFilter)
  418. }
  419. // Filter peers by addr or pubkey with an ABCI query.
  420. // If the query return code is OK, add peer.
  421. if config.FilterPeers {
  422. connFilters = append(
  423. connFilters,
  424. // ABCI query for address filtering.
  425. func(_ p2p.ConnSet, c net.Conn, _ []net.IP) error {
  426. res, err := proxyApp.Query().QuerySync(context.Background(), abci.RequestQuery{
  427. Path: fmt.Sprintf("/p2p/filter/addr/%s", c.RemoteAddr().String()),
  428. })
  429. if err != nil {
  430. return err
  431. }
  432. if res.IsErr() {
  433. return fmt.Errorf("error querying abci app: %v", res)
  434. }
  435. return nil
  436. },
  437. )
  438. peerFilters = append(
  439. peerFilters,
  440. // ABCI query for ID filtering.
  441. func(_ p2p.IPeerSet, p p2p.Peer) error {
  442. res, err := proxyApp.Query().QuerySync(context.Background(), abci.RequestQuery{
  443. Path: fmt.Sprintf("/p2p/filter/id/%s", p.ID()),
  444. })
  445. if err != nil {
  446. return err
  447. }
  448. if res.IsErr() {
  449. return fmt.Errorf("error querying abci app: %v", res)
  450. }
  451. return nil
  452. },
  453. )
  454. }
  455. transport := p2p.NewMConnTransport(
  456. logger, nodeInfo, nodeKey.PrivKey, p2p.MConnConfig(config.P2P),
  457. p2p.MConnTransportConnFilters(connFilters...),
  458. p2p.MConnTransportMaxIncomingConnections(config.P2P.MaxNumInboundPeers+
  459. len(splitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " "))),
  460. )
  461. return transport, peerFilters
  462. }
  463. func createSwitch(config *cfg.Config,
  464. transport p2p.Transport,
  465. p2pMetrics *p2p.Metrics,
  466. peerFilters []p2p.PeerFilterFunc,
  467. mempoolReactor *mempl.Reactor,
  468. bcReactor p2p.Reactor,
  469. stateSyncReactor *p2p.ReactorShim,
  470. consensusReactor *cs.Reactor,
  471. evidenceReactor *evidence.Reactor,
  472. nodeInfo p2p.NodeInfo,
  473. nodeKey p2p.NodeKey,
  474. p2pLogger log.Logger) *p2p.Switch {
  475. sw := p2p.NewSwitch(
  476. config.P2P,
  477. transport,
  478. p2p.WithMetrics(p2pMetrics),
  479. p2p.SwitchPeerFilters(peerFilters...),
  480. )
  481. sw.SetLogger(p2pLogger)
  482. sw.AddReactor("MEMPOOL", mempoolReactor)
  483. sw.AddReactor("BLOCKCHAIN", bcReactor)
  484. sw.AddReactor("CONSENSUS", consensusReactor)
  485. sw.AddReactor("EVIDENCE", evidenceReactor)
  486. sw.AddReactor("STATESYNC", stateSyncReactor)
  487. sw.SetNodeInfo(nodeInfo)
  488. sw.SetNodeKey(nodeKey)
  489. p2pLogger.Info("P2P Node ID", "ID", nodeKey.ID, "file", config.NodeKeyFile())
  490. return sw
  491. }
  492. func createAddrBookAndSetOnSwitch(config *cfg.Config, sw *p2p.Switch,
  493. p2pLogger log.Logger, nodeKey p2p.NodeKey) (pex.AddrBook, error) {
  494. addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict)
  495. addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile()))
  496. // Add ourselves to addrbook to prevent dialing ourselves
  497. if config.P2P.ExternalAddress != "" {
  498. addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID, config.P2P.ExternalAddress))
  499. if err != nil {
  500. return nil, fmt.Errorf("p2p.external_address is incorrect: %w", err)
  501. }
  502. addrBook.AddOurAddress(addr)
  503. }
  504. if config.P2P.ListenAddress != "" {
  505. addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID, config.P2P.ListenAddress))
  506. if err != nil {
  507. return nil, fmt.Errorf("p2p.laddr is incorrect: %w", err)
  508. }
  509. addrBook.AddOurAddress(addr)
  510. }
  511. sw.SetAddrBook(addrBook)
  512. return addrBook, nil
  513. }
  514. func createPEXReactorAndAddToSwitch(addrBook pex.AddrBook, config *cfg.Config,
  515. sw *p2p.Switch, logger log.Logger) *pex.Reactor {
  516. // TODO persistent peers ? so we can have their DNS addrs saved
  517. pexReactor := pex.NewReactor(addrBook,
  518. &pex.ReactorConfig{
  519. Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "),
  520. SeedMode: config.P2P.SeedMode,
  521. // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000
  522. // blocks assuming 10s blocks ~ 28 hours.
  523. // TODO (melekes): make it dynamic based on the actual block latencies
  524. // from the live network.
  525. // https://github.com/tendermint/tendermint/issues/3523
  526. SeedDisconnectWaitPeriod: 28 * time.Hour,
  527. PersistentPeersMaxDialPeriod: config.P2P.PersistentPeersMaxDialPeriod,
  528. })
  529. pexReactor.SetLogger(logger.With("module", "pex"))
  530. sw.AddReactor("PEX", pexReactor)
  531. return pexReactor
  532. }
  533. // startStateSync starts an asynchronous state sync process, then switches to fast sync mode.
  534. func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reactor,
  535. stateProvider statesync.StateProvider, config *cfg.StateSyncConfig, fastSync bool,
  536. stateStore sm.Store, blockStore *store.BlockStore, state sm.State) error {
  537. ssR.Logger.Info("Starting state sync")
  538. if stateProvider == nil {
  539. var err error
  540. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  541. defer cancel()
  542. stateProvider, err = statesync.NewLightClientStateProvider(
  543. ctx,
  544. state.ChainID, state.Version, state.InitialHeight,
  545. config.RPCServers, light.TrustOptions{
  546. Period: config.TrustPeriod,
  547. Height: config.TrustHeight,
  548. Hash: config.TrustHashBytes(),
  549. }, ssR.Logger.With("module", "light"))
  550. if err != nil {
  551. return fmt.Errorf("failed to set up light client state provider: %w", err)
  552. }
  553. }
  554. go func() {
  555. state, commit, err := ssR.Sync(stateProvider, config.DiscoveryTime)
  556. if err != nil {
  557. ssR.Logger.Error("State sync failed", "err", err)
  558. return
  559. }
  560. err = stateStore.Bootstrap(state)
  561. if err != nil {
  562. ssR.Logger.Error("Failed to bootstrap node with new state", "err", err)
  563. return
  564. }
  565. err = blockStore.SaveSeenCommit(state.LastBlockHeight, commit)
  566. if err != nil {
  567. ssR.Logger.Error("Failed to store last seen commit", "err", err)
  568. return
  569. }
  570. if fastSync {
  571. // FIXME Very ugly to have these metrics bleed through here.
  572. conR.Metrics.StateSyncing.Set(0)
  573. conR.Metrics.FastSyncing.Set(1)
  574. err = bcR.SwitchToFastSync(state)
  575. if err != nil {
  576. ssR.Logger.Error("Failed to switch to fast sync", "err", err)
  577. return
  578. }
  579. } else {
  580. conR.SwitchToConsensus(state, true)
  581. }
  582. }()
  583. return nil
  584. }
  585. // NewNode returns a new, ready to go, Tendermint Node.
  586. func NewNode(config *cfg.Config,
  587. privValidator types.PrivValidator,
  588. nodeKey p2p.NodeKey,
  589. clientCreator proxy.ClientCreator,
  590. genesisDocProvider GenesisDocProvider,
  591. dbProvider DBProvider,
  592. metricsProvider MetricsProvider,
  593. logger log.Logger,
  594. misbehaviors map[int64]cs.Misbehavior,
  595. options ...Option) (*Node, error) {
  596. blockStore, stateDB, err := initDBs(config, dbProvider)
  597. if err != nil {
  598. return nil, err
  599. }
  600. stateStore := sm.NewStore(stateDB)
  601. state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider)
  602. if err != nil {
  603. return nil, err
  604. }
  605. // Create the proxyApp and establish connections to the ABCI app (consensus, mempool, query).
  606. proxyApp, err := createAndStartProxyAppConns(clientCreator, logger)
  607. if err != nil {
  608. return nil, err
  609. }
  610. // EventBus and IndexerService must be started before the handshake because
  611. // we might need to index the txs of the replayed block as this might not have happened
  612. // when the node stopped last time (i.e. the node stopped after it saved the block
  613. // but before it indexed the txs, or, endblocker panicked)
  614. eventBus, err := createAndStartEventBus(logger)
  615. if err != nil {
  616. return nil, err
  617. }
  618. // Transaction indexing
  619. indexerService, txIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger)
  620. if err != nil {
  621. return nil, err
  622. }
  623. // If an address is provided, listen on the socket for a connection from an
  624. // external signing process.
  625. if config.PrivValidatorListenAddr != "" {
  626. // FIXME: we should start services inside OnStart
  627. privValidator, err = createAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, genDoc.ChainID, logger)
  628. if err != nil {
  629. return nil, fmt.Errorf("error with private validator socket client: %w", err)
  630. }
  631. }
  632. pubKey, err := privValidator.GetPubKey()
  633. if err != nil {
  634. return nil, fmt.Errorf("can't get pubkey: %w", err)
  635. }
  636. // Determine whether we should do state and/or fast sync.
  637. // We don't fast-sync when the only validator is us.
  638. fastSync := config.FastSyncMode && !onlyValidatorIsUs(state, pubKey)
  639. stateSync := config.StateSync.Enable && !onlyValidatorIsUs(state, pubKey)
  640. if stateSync && state.LastBlockHeight > 0 {
  641. logger.Info("Found local state with non-zero height, skipping state sync")
  642. stateSync = false
  643. }
  644. // Create the handshaker, which calls RequestInfo, sets the AppVersion on the state,
  645. // and replays any blocks as necessary to sync tendermint with the app.
  646. consensusLogger := logger.With("module", "consensus")
  647. if !stateSync {
  648. if err := doHandshake(stateStore, state, blockStore, genDoc, eventBus, proxyApp, consensusLogger); err != nil {
  649. return nil, err
  650. }
  651. // Reload the state. It will have the Version.Consensus.App set by the
  652. // Handshake, and may have other modifications as well (ie. depending on
  653. // what happened during block replay).
  654. state, err = stateStore.Load()
  655. if err != nil {
  656. return nil, fmt.Errorf("cannot load state: %w", err)
  657. }
  658. }
  659. logNodeStartupInfo(state, pubKey, logger, consensusLogger)
  660. csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID)
  661. // Make MempoolReactor
  662. mempoolReactor, mempool := createMempoolAndMempoolReactor(config, proxyApp, state, memplMetrics, logger)
  663. // Make Evidence Reactor
  664. evidenceReactor, evidencePool, err := createEvidenceReactor(config, dbProvider, stateDB, blockStore, logger)
  665. if err != nil {
  666. return nil, err
  667. }
  668. // make block executor for consensus and blockchain reactors to execute blocks
  669. blockExec := sm.NewBlockExecutor(
  670. stateStore,
  671. logger.With("module", "state"),
  672. proxyApp.Consensus(),
  673. mempool,
  674. evidencePool,
  675. sm.BlockExecutorWithMetrics(smMetrics),
  676. )
  677. // Make BlockchainReactor. Don't start fast sync if we're doing a state sync first.
  678. bcReactor, err := createBlockchainReactor(config, state, blockExec, blockStore, fastSync && !stateSync, logger)
  679. if err != nil {
  680. return nil, fmt.Errorf("could not create blockchain reactor: %w", err)
  681. }
  682. // Make ConsensusReactor. Don't enable fully if doing a state sync and/or fast sync first.
  683. // FIXME We need to update metrics here, since other reactors don't have access to them.
  684. if stateSync {
  685. csMetrics.StateSyncing.Set(1)
  686. } else if fastSync {
  687. csMetrics.FastSyncing.Set(1)
  688. }
  689. logger.Info("Setting up maverick consensus reactor", "Misbehaviors", misbehaviors)
  690. consensusReactor, consensusState := createConsensusReactor(
  691. config, state, blockExec, blockStore, mempool, evidencePool,
  692. privValidator, csMetrics, stateSync || fastSync, eventBus, consensusLogger, misbehaviors)
  693. // Set up state sync reactor, and schedule a sync if requested.
  694. // FIXME The way we do phased startups (e.g. replay -> fast sync -> consensus) is very messy,
  695. // we should clean this whole thing up. See:
  696. // https://github.com/tendermint/tendermint/issues/4644
  697. stateSyncReactorShim := p2p.NewReactorShim("StateSyncShim", statesync.ChannelShims)
  698. stateSyncReactorShim.SetLogger(logger.With("module", "statesync"))
  699. stateSyncReactor := statesync.NewReactor(
  700. stateSyncReactorShim.Logger,
  701. proxyApp.Snapshot(),
  702. proxyApp.Query(),
  703. stateSyncReactorShim.GetChannel(statesync.SnapshotChannel),
  704. stateSyncReactorShim.GetChannel(statesync.ChunkChannel),
  705. stateSyncReactorShim.PeerUpdates,
  706. config.StateSync.TempDir,
  707. )
  708. nodeInfo, err := makeNodeInfo(config, nodeKey, txIndexer, genDoc, state)
  709. if err != nil {
  710. return nil, err
  711. }
  712. // Setup Transport and Switch.
  713. p2pLogger := logger.With("module", "p2p")
  714. transport, peerFilters := createTransport(p2pLogger, config, nodeInfo, nodeKey, proxyApp)
  715. sw := createSwitch(
  716. config, transport, p2pMetrics, peerFilters, mempoolReactor, bcReactor,
  717. stateSyncReactorShim, consensusReactor, evidenceReactor, nodeInfo, nodeKey, p2pLogger,
  718. )
  719. err = sw.AddPersistentPeers(splitAndTrimEmpty(config.P2P.PersistentPeers, ",", " "))
  720. if err != nil {
  721. return nil, fmt.Errorf("could not add peers from persistent-peers field: %w", err)
  722. }
  723. err = sw.AddUnconditionalPeerIDs(splitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " "))
  724. if err != nil {
  725. return nil, fmt.Errorf("could not add peer ids from unconditional_peer_ids field: %w", err)
  726. }
  727. addrBook, err := createAddrBookAndSetOnSwitch(config, sw, p2pLogger, nodeKey)
  728. if err != nil {
  729. return nil, fmt.Errorf("could not create addrbook: %w", err)
  730. }
  731. // Optionally, start the pex reactor
  732. //
  733. // TODO:
  734. //
  735. // We need to set Seeds and PersistentPeers on the switch,
  736. // since it needs to be able to use these (and their DNS names)
  737. // even if the PEX is off. We can include the DNS name in the NetAddress,
  738. // but it would still be nice to have a clear list of the current "PersistentPeers"
  739. // somewhere that we can return with net_info.
  740. //
  741. // If PEX is on, it should handle dialing the seeds. Otherwise the switch does it.
  742. // Note we currently use the addrBook regardless at least for AddOurAddress
  743. var pexReactor *pex.Reactor
  744. if config.P2P.PexReactor {
  745. pexReactor = createPEXReactorAndAddToSwitch(addrBook, config, sw, logger)
  746. }
  747. if config.RPC.PprofListenAddress != "" {
  748. go func() {
  749. logger.Info("Starting pprof server", "laddr", config.RPC.PprofListenAddress)
  750. logger.Error("pprof server error", "err", http.ListenAndServe(config.RPC.PprofListenAddress, nil))
  751. }()
  752. }
  753. node := &Node{
  754. config: config,
  755. genesisDoc: genDoc,
  756. privValidator: privValidator,
  757. transport: transport,
  758. sw: sw,
  759. addrBook: addrBook,
  760. nodeInfo: nodeInfo,
  761. nodeKey: nodeKey,
  762. stateStore: stateStore,
  763. blockStore: blockStore,
  764. bcReactor: bcReactor,
  765. mempoolReactor: mempoolReactor,
  766. mempool: mempool,
  767. consensusState: consensusState,
  768. consensusReactor: consensusReactor,
  769. stateSyncReactor: stateSyncReactor,
  770. stateSync: stateSync,
  771. stateSyncGenesis: state, // Shouldn't be necessary, but need a way to pass the genesis state
  772. pexReactor: pexReactor,
  773. evidencePool: evidencePool,
  774. proxyApp: proxyApp,
  775. txIndexer: txIndexer,
  776. indexerService: indexerService,
  777. eventBus: eventBus,
  778. }
  779. node.BaseService = *service.NewBaseService(logger, "Node", node)
  780. for _, option := range options {
  781. option(node)
  782. }
  783. return node, nil
  784. }
  785. // OnStart starts the Node. It implements service.Service.
  786. func (n *Node) OnStart() error {
  787. now := tmtime.Now()
  788. genTime := n.genesisDoc.GenesisTime
  789. if genTime.After(now) {
  790. n.Logger.Info("Genesis time is in the future. Sleeping until then...", "genTime", genTime)
  791. time.Sleep(genTime.Sub(now))
  792. }
  793. // Add private IDs to addrbook to block those peers being added
  794. n.addrBook.AddPrivateIDs(splitAndTrimEmpty(n.config.P2P.PrivatePeerIDs, ",", " "))
  795. // Start the RPC server before the P2P server
  796. // so we can eg. receive txs for the first block
  797. if n.config.RPC.ListenAddress != "" {
  798. listeners, err := n.startRPC()
  799. if err != nil {
  800. return err
  801. }
  802. n.rpcListeners = listeners
  803. }
  804. if n.config.Instrumentation.Prometheus &&
  805. n.config.Instrumentation.PrometheusListenAddr != "" {
  806. n.prometheusSrv = n.startPrometheusServer(n.config.Instrumentation.PrometheusListenAddr)
  807. }
  808. // Start the mempool.
  809. if n.config.Mempool.WalEnabled() {
  810. err := n.mempool.InitWAL()
  811. if err != nil {
  812. return fmt.Errorf("init mempool WAL: %w", err)
  813. }
  814. }
  815. // Start the switch (the P2P server).
  816. err := n.sw.Start()
  817. if err != nil {
  818. return err
  819. }
  820. // Start the transport.
  821. addr, err := p2p.NewNetAddressString(p2p.IDAddressString(n.nodeKey.ID, n.config.P2P.ListenAddress))
  822. if err != nil {
  823. return err
  824. }
  825. if err := n.transport.Listen(addr.Endpoint()); err != nil {
  826. return err
  827. }
  828. n.isListening = true
  829. // Start the real state sync reactor separately since the switch uses the shim.
  830. if err := n.stateSyncReactor.Start(); err != nil {
  831. return err
  832. }
  833. // Always connect to persistent peers
  834. err = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " "))
  835. if err != nil {
  836. return fmt.Errorf("could not dial peers from persistent-peers field: %w", err)
  837. }
  838. // Run state sync
  839. if n.stateSync {
  840. bcR, ok := n.bcReactor.(fastSyncReactor)
  841. if !ok {
  842. return fmt.Errorf("this blockchain reactor does not support switching from state sync")
  843. }
  844. err := startStateSync(n.stateSyncReactor, bcR, n.consensusReactor, n.stateSyncProvider,
  845. n.config.StateSync, n.config.FastSyncMode, n.stateStore, n.blockStore, n.stateSyncGenesis)
  846. if err != nil {
  847. return fmt.Errorf("failed to start state sync: %w", err)
  848. }
  849. }
  850. return nil
  851. }
  852. // OnStop stops the Node. It implements service.Service.
  853. func (n *Node) OnStop() {
  854. n.BaseService.OnStop()
  855. n.Logger.Info("Stopping Node")
  856. // first stop the non-reactor services
  857. if err := n.eventBus.Stop(); err != nil {
  858. n.Logger.Error("Error closing eventBus", "err", err)
  859. }
  860. if err := n.indexerService.Stop(); err != nil {
  861. n.Logger.Error("Error closing indexerService", "err", err)
  862. }
  863. // now stop the reactors
  864. if err := n.sw.Stop(); err != nil {
  865. n.Logger.Error("Error closing switch", "err", err)
  866. }
  867. // Stop the real state sync reactor separately since the switch uses the shim.
  868. if err := n.stateSyncReactor.Stop(); err != nil {
  869. n.Logger.Error("failed to stop state sync service", "err", err)
  870. }
  871. // stop mempool WAL
  872. if n.config.Mempool.WalEnabled() {
  873. n.mempool.CloseWAL()
  874. }
  875. if err := n.transport.Close(); err != nil {
  876. n.Logger.Error("Error closing transport", "err", err)
  877. }
  878. n.isListening = false
  879. // finally stop the listeners / external services
  880. for _, l := range n.rpcListeners {
  881. n.Logger.Info("Closing rpc listener", "listener", l)
  882. if err := l.Close(); err != nil {
  883. n.Logger.Error("Error closing listener", "listener", l, "err", err)
  884. }
  885. }
  886. if pvsc, ok := n.privValidator.(service.Service); ok {
  887. if err := pvsc.Stop(); err != nil {
  888. n.Logger.Error("Error closing private validator", "err", err)
  889. }
  890. }
  891. if n.prometheusSrv != nil {
  892. if err := n.prometheusSrv.Shutdown(context.Background()); err != nil {
  893. // Error from closing listeners, or context timeout:
  894. n.Logger.Error("Prometheus HTTP server Shutdown", "err", err)
  895. }
  896. }
  897. }
  898. // ConfigureRPC makes sure RPC has all the objects it needs to operate.
  899. func (n *Node) ConfigureRPC() error {
  900. pubKey, err := n.privValidator.GetPubKey()
  901. if err != nil {
  902. return fmt.Errorf("can't get pubkey: %w", err)
  903. }
  904. rpccore.SetEnvironment(&rpccore.Environment{
  905. ProxyAppQuery: n.proxyApp.Query(),
  906. ProxyAppMempool: n.proxyApp.Mempool(),
  907. StateStore: n.stateStore,
  908. BlockStore: n.blockStore,
  909. EvidencePool: n.evidencePool,
  910. ConsensusState: n.consensusState,
  911. P2PPeers: n.sw,
  912. P2PTransport: n,
  913. PubKey: pubKey,
  914. GenDoc: n.genesisDoc,
  915. TxIndexer: n.txIndexer,
  916. ConsensusReactor: &consensus.Reactor{},
  917. EventBus: n.eventBus,
  918. Mempool: n.mempool,
  919. Logger: n.Logger.With("module", "rpc"),
  920. Config: *n.config.RPC,
  921. })
  922. return nil
  923. }
  924. func (n *Node) startRPC() ([]net.Listener, error) {
  925. err := n.ConfigureRPC()
  926. if err != nil {
  927. return nil, err
  928. }
  929. listenAddrs := splitAndTrimEmpty(n.config.RPC.ListenAddress, ",", " ")
  930. if n.config.RPC.Unsafe {
  931. rpccore.AddUnsafeRoutes()
  932. }
  933. config := rpcserver.DefaultConfig()
  934. config.MaxBodyBytes = n.config.RPC.MaxBodyBytes
  935. config.MaxHeaderBytes = n.config.RPC.MaxHeaderBytes
  936. config.MaxOpenConnections = n.config.RPC.MaxOpenConnections
  937. // If necessary adjust global WriteTimeout to ensure it's greater than
  938. // TimeoutBroadcastTxCommit.
  939. // See https://github.com/tendermint/tendermint/issues/3435
  940. if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit {
  941. config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second
  942. }
  943. // we may expose the rpc over both a unix and tcp socket
  944. listeners := make([]net.Listener, len(listenAddrs))
  945. for i, listenAddr := range listenAddrs {
  946. mux := http.NewServeMux()
  947. rpcLogger := n.Logger.With("module", "rpc-server")
  948. wmLogger := rpcLogger.With("protocol", "websocket")
  949. wm := rpcserver.NewWebsocketManager(rpccore.Routes,
  950. rpcserver.OnDisconnect(func(remoteAddr string) {
  951. err := n.eventBus.UnsubscribeAll(context.Background(), remoteAddr)
  952. if err != nil && err != tmpubsub.ErrSubscriptionNotFound {
  953. wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err)
  954. }
  955. }),
  956. rpcserver.ReadLimit(config.MaxBodyBytes),
  957. )
  958. wm.SetLogger(wmLogger)
  959. mux.HandleFunc("/websocket", wm.WebsocketHandler)
  960. rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, rpcLogger)
  961. listener, err := rpcserver.Listen(
  962. listenAddr,
  963. config,
  964. )
  965. if err != nil {
  966. return nil, err
  967. }
  968. var rootHandler http.Handler = mux
  969. if n.config.RPC.IsCorsEnabled() {
  970. corsMiddleware := cors.New(cors.Options{
  971. AllowedOrigins: n.config.RPC.CORSAllowedOrigins,
  972. AllowedMethods: n.config.RPC.CORSAllowedMethods,
  973. AllowedHeaders: n.config.RPC.CORSAllowedHeaders,
  974. })
  975. rootHandler = corsMiddleware.Handler(mux)
  976. }
  977. if n.config.RPC.IsTLSEnabled() {
  978. go func() {
  979. if err := rpcserver.ServeTLS(
  980. listener,
  981. rootHandler,
  982. n.config.RPC.CertFile(),
  983. n.config.RPC.KeyFile(),
  984. rpcLogger,
  985. config,
  986. ); err != nil {
  987. n.Logger.Error("Error serving server with TLS", "err", err)
  988. }
  989. }()
  990. } else {
  991. go func() {
  992. if err := rpcserver.Serve(
  993. listener,
  994. rootHandler,
  995. rpcLogger,
  996. config,
  997. ); err != nil {
  998. n.Logger.Error("Error serving server", "err", err)
  999. }
  1000. }()
  1001. }
  1002. listeners[i] = listener
  1003. }
  1004. // we expose a simplified api over grpc for convenience to app devs
  1005. grpcListenAddr := n.config.RPC.GRPCListenAddress
  1006. if grpcListenAddr != "" {
  1007. config := rpcserver.DefaultConfig()
  1008. config.MaxBodyBytes = n.config.RPC.MaxBodyBytes
  1009. config.MaxHeaderBytes = n.config.RPC.MaxHeaderBytes
  1010. // NOTE: GRPCMaxOpenConnections is used, not MaxOpenConnections
  1011. config.MaxOpenConnections = n.config.RPC.GRPCMaxOpenConnections
  1012. // If necessary adjust global WriteTimeout to ensure it's greater than
  1013. // TimeoutBroadcastTxCommit.
  1014. // See https://github.com/tendermint/tendermint/issues/3435
  1015. if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit {
  1016. config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second
  1017. }
  1018. listener, err := rpcserver.Listen(grpcListenAddr, config)
  1019. if err != nil {
  1020. return nil, err
  1021. }
  1022. go func() {
  1023. if err := grpccore.StartGRPCServer(listener); err != nil {
  1024. n.Logger.Error("Error starting gRPC server", "err", err)
  1025. }
  1026. }()
  1027. listeners = append(listeners, listener)
  1028. }
  1029. return listeners, nil
  1030. }
  1031. // startPrometheusServer starts a Prometheus HTTP server, listening for metrics
  1032. // collectors on addr.
  1033. func (n *Node) startPrometheusServer(addr string) *http.Server {
  1034. srv := &http.Server{
  1035. Addr: addr,
  1036. Handler: promhttp.InstrumentMetricHandler(
  1037. prometheus.DefaultRegisterer, promhttp.HandlerFor(
  1038. prometheus.DefaultGatherer,
  1039. promhttp.HandlerOpts{MaxRequestsInFlight: n.config.Instrumentation.MaxOpenConnections},
  1040. ),
  1041. ),
  1042. }
  1043. go func() {
  1044. if err := srv.ListenAndServe(); err != http.ErrServerClosed {
  1045. // Error starting or closing listener:
  1046. n.Logger.Error("Prometheus HTTP server ListenAndServe", "err", err)
  1047. }
  1048. }()
  1049. return srv
  1050. }
  1051. // Switch returns the Node's Switch.
  1052. func (n *Node) Switch() *p2p.Switch {
  1053. return n.sw
  1054. }
  1055. // BlockStore returns the Node's BlockStore.
  1056. func (n *Node) BlockStore() *store.BlockStore {
  1057. return n.blockStore
  1058. }
  1059. // ConsensusState returns the Node's ConsensusState.
  1060. func (n *Node) ConsensusState() *cs.State {
  1061. return n.consensusState
  1062. }
  1063. // ConsensusReactor returns the Node's ConsensusReactor.
  1064. func (n *Node) ConsensusReactor() *cs.Reactor {
  1065. return n.consensusReactor
  1066. }
  1067. // MempoolReactor returns the Node's mempool reactor.
  1068. func (n *Node) MempoolReactor() *mempl.Reactor {
  1069. return n.mempoolReactor
  1070. }
  1071. // Mempool returns the Node's mempool.
  1072. func (n *Node) Mempool() mempl.Mempool {
  1073. return n.mempool
  1074. }
  1075. // PEXReactor returns the Node's PEXReactor. It returns nil if PEX is disabled.
  1076. func (n *Node) PEXReactor() *pex.Reactor {
  1077. return n.pexReactor
  1078. }
  1079. // EvidencePool returns the Node's EvidencePool.
  1080. func (n *Node) EvidencePool() *evidence.Pool {
  1081. return n.evidencePool
  1082. }
  1083. // EventBus returns the Node's EventBus.
  1084. func (n *Node) EventBus() *types.EventBus {
  1085. return n.eventBus
  1086. }
  1087. // PrivValidator returns the Node's PrivValidator.
  1088. // XXX: for convenience only!
  1089. func (n *Node) PrivValidator() types.PrivValidator {
  1090. return n.privValidator
  1091. }
  1092. // GenesisDoc returns the Node's GenesisDoc.
  1093. func (n *Node) GenesisDoc() *types.GenesisDoc {
  1094. return n.genesisDoc
  1095. }
  1096. // ProxyApp returns the Node's AppConns, representing its connections to the ABCI application.
  1097. func (n *Node) ProxyApp() proxy.AppConns {
  1098. return n.proxyApp
  1099. }
  1100. // Config returns the Node's config.
  1101. func (n *Node) Config() *cfg.Config {
  1102. return n.config
  1103. }
  1104. //------------------------------------------------------------------------------
  1105. func (n *Node) Listeners() []string {
  1106. return []string{
  1107. fmt.Sprintf("Listener(@%v)", n.config.P2P.ExternalAddress),
  1108. }
  1109. }
  1110. func (n *Node) IsListening() bool {
  1111. return n.isListening
  1112. }
  1113. // NodeInfo returns the Node's Info from the Switch.
  1114. func (n *Node) NodeInfo() p2p.NodeInfo {
  1115. return n.nodeInfo
  1116. }
  1117. func makeNodeInfo(
  1118. config *cfg.Config,
  1119. nodeKey p2p.NodeKey,
  1120. txIndexer txindex.TxIndexer,
  1121. genDoc *types.GenesisDoc,
  1122. state sm.State,
  1123. ) (p2p.NodeInfo, error) {
  1124. txIndexerStatus := "on"
  1125. if _, ok := txIndexer.(*null.TxIndex); ok {
  1126. txIndexerStatus = "off"
  1127. }
  1128. var bcChannel byte
  1129. switch config.FastSync.Version {
  1130. case "v0":
  1131. bcChannel = bcv0.BlockchainChannel
  1132. case "v2":
  1133. bcChannel = bcv2.BlockchainChannel
  1134. default:
  1135. return p2p.NodeInfo{}, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version)
  1136. }
  1137. nodeInfo := p2p.NodeInfo{
  1138. ProtocolVersion: p2p.NewProtocolVersion(
  1139. version.P2PProtocol, // global
  1140. state.Version.Consensus.Block,
  1141. state.Version.Consensus.App,
  1142. ),
  1143. NodeID: nodeKey.ID,
  1144. Network: genDoc.ChainID,
  1145. Version: version.TMCoreSemVer,
  1146. Channels: []byte{
  1147. bcChannel,
  1148. cs.StateChannel, cs.DataChannel, cs.VoteChannel, cs.VoteSetBitsChannel,
  1149. mempl.MempoolChannel,
  1150. evidence.EvidenceChannel,
  1151. byte(statesync.SnapshotChannel), byte(statesync.ChunkChannel),
  1152. },
  1153. Moniker: config.Moniker,
  1154. Other: p2p.NodeInfoOther{
  1155. TxIndex: txIndexerStatus,
  1156. RPCAddress: config.RPC.ListenAddress,
  1157. },
  1158. }
  1159. if config.P2P.PexReactor {
  1160. nodeInfo.Channels = append(nodeInfo.Channels, pex.PexChannel)
  1161. }
  1162. lAddr := config.P2P.ExternalAddress
  1163. if lAddr == "" {
  1164. lAddr = config.P2P.ListenAddress
  1165. }
  1166. nodeInfo.ListenAddr = lAddr
  1167. err := nodeInfo.Validate()
  1168. return nodeInfo, err
  1169. }
  1170. //------------------------------------------------------------------------------
  1171. var (
  1172. genesisDocKey = []byte("genesisDoc")
  1173. )
  1174. // LoadStateFromDBOrGenesisDocProvider attempts to load the state from the
  1175. // database, or creates one using the given genesisDocProvider and persists the
  1176. // result to the database. On success this also returns the genesis doc loaded
  1177. // through the given provider.
  1178. func LoadStateFromDBOrGenesisDocProvider(
  1179. stateDB dbm.DB,
  1180. genesisDocProvider GenesisDocProvider,
  1181. ) (sm.State, *types.GenesisDoc, error) {
  1182. // Get genesis doc
  1183. genDoc, err := loadGenesisDoc(stateDB)
  1184. if err != nil {
  1185. genDoc, err = genesisDocProvider()
  1186. if err != nil {
  1187. return sm.State{}, nil, err
  1188. }
  1189. // save genesis doc to prevent a certain class of user errors (e.g. when it
  1190. // was changed, accidentally or not). Also good for audit trail.
  1191. saveGenesisDoc(stateDB, genDoc)
  1192. }
  1193. stateStore := sm.NewStore(stateDB)
  1194. state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
  1195. if err != nil {
  1196. return sm.State{}, nil, err
  1197. }
  1198. return state, genDoc, nil
  1199. }
  1200. // panics if failed to unmarshal bytes
  1201. func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) {
  1202. b, err := db.Get(genesisDocKey)
  1203. if err != nil {
  1204. panic(err)
  1205. }
  1206. if len(b) == 0 {
  1207. return nil, errors.New("genesis doc not found")
  1208. }
  1209. var genDoc *types.GenesisDoc
  1210. err = tmjson.Unmarshal(b, &genDoc)
  1211. if err != nil {
  1212. panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, b))
  1213. }
  1214. return genDoc, nil
  1215. }
  1216. // panics if failed to marshal the given genesis document
  1217. func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) {
  1218. b, err := tmjson.Marshal(genDoc)
  1219. if err != nil {
  1220. panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err))
  1221. }
  1222. if err := db.SetSync(genesisDocKey, b); err != nil {
  1223. panic(fmt.Sprintf("Failed to save genesis doc: %v", err))
  1224. }
  1225. }
  1226. func createAndStartPrivValidatorSocketClient(
  1227. listenAddr,
  1228. chainID string,
  1229. logger log.Logger,
  1230. ) (types.PrivValidator, error) {
  1231. pve, err := privval.NewSignerListener(listenAddr, logger)
  1232. if err != nil {
  1233. return nil, fmt.Errorf("failed to start private validator: %w", err)
  1234. }
  1235. pvsc, err := privval.NewSignerClient(pve, chainID)
  1236. if err != nil {
  1237. return nil, fmt.Errorf("failed to start private validator: %w", err)
  1238. }
  1239. // try to get a pubkey from private validate first time
  1240. _, err = pvsc.GetPubKey()
  1241. if err != nil {
  1242. return nil, fmt.Errorf("can't get pubkey: %w", err)
  1243. }
  1244. const (
  1245. retries = 50 // 50 * 100ms = 5s total
  1246. timeout = 100 * time.Millisecond
  1247. )
  1248. pvscWithRetries := privval.NewRetrySignerClient(pvsc, retries, timeout)
  1249. return pvscWithRetries, nil
  1250. }
  1251. // splitAndTrimEmpty slices s into all subslices separated by sep and returns a
  1252. // slice of the string s with all leading and trailing Unicode code points
  1253. // contained in cutset removed. If sep is empty, SplitAndTrim splits after each
  1254. // UTF-8 sequence. First part is equivalent to strings.SplitN with a count of
  1255. // -1. also filter out empty strings, only return non-empty strings.
  1256. func splitAndTrimEmpty(s, sep, cutset string) []string {
  1257. if s == "" {
  1258. return []string{}
  1259. }
  1260. spl := strings.Split(s, sep)
  1261. nonEmptyStrings := make([]string, 0, len(spl))
  1262. for i := 0; i < len(spl); i++ {
  1263. element := strings.Trim(spl[i], cutset)
  1264. if element != "" {
  1265. nonEmptyStrings = append(nonEmptyStrings, element)
  1266. }
  1267. }
  1268. return nonEmptyStrings
  1269. }