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.

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