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.

216 lines
4.7 KiB

  1. package privval
  2. import (
  3. "errors"
  4. "net"
  5. "time"
  6. "github.com/tendermint/tendermint/crypto/ed25519"
  7. cmn "github.com/tendermint/tendermint/libs/common"
  8. "github.com/tendermint/tendermint/libs/log"
  9. p2pconn "github.com/tendermint/tendermint/p2p/conn"
  10. "github.com/tendermint/tendermint/types"
  11. )
  12. const (
  13. defaultAcceptDeadlineSeconds = 3
  14. defaultConnDeadlineSeconds = 3
  15. defaultConnHeartBeatSeconds = 2
  16. defaultDialRetries = 10
  17. )
  18. // Socket errors.
  19. var (
  20. ErrDialRetryMax = errors.New("dialed maximum retries")
  21. ErrConnTimeout = errors.New("remote signer timed out")
  22. ErrUnexpectedResponse = errors.New("received unexpected response")
  23. )
  24. var (
  25. acceptDeadline = time.Second * defaultAcceptDeadlineSeconds
  26. connTimeout = time.Second * defaultConnDeadlineSeconds
  27. connHeartbeat = time.Second * defaultConnHeartBeatSeconds
  28. )
  29. // TCPValOption sets an optional parameter on the SocketPV.
  30. type TCPValOption func(*TCPVal)
  31. // TCPValAcceptDeadline sets the deadline for the TCPVal listener.
  32. // A zero time value disables the deadline.
  33. func TCPValAcceptDeadline(deadline time.Duration) TCPValOption {
  34. return func(sc *TCPVal) { sc.acceptDeadline = deadline }
  35. }
  36. // TCPValConnTimeout sets the read and write timeout for connections
  37. // from external signing processes.
  38. func TCPValConnTimeout(timeout time.Duration) TCPValOption {
  39. return func(sc *TCPVal) { sc.connTimeout = timeout }
  40. }
  41. // TCPValHeartbeat sets the period on which to check the liveness of the
  42. // connected Signer connections.
  43. func TCPValHeartbeat(period time.Duration) TCPValOption {
  44. return func(sc *TCPVal) { sc.connHeartbeat = period }
  45. }
  46. // TCPVal implements PrivValidator, it uses a socket to request signatures
  47. // from an external process.
  48. type TCPVal struct {
  49. cmn.BaseService
  50. *RemoteSignerClient
  51. addr string
  52. acceptDeadline time.Duration
  53. connTimeout time.Duration
  54. connHeartbeat time.Duration
  55. privKey ed25519.PrivKeyEd25519
  56. conn net.Conn
  57. listener net.Listener
  58. cancelPing chan struct{}
  59. pingTicker *time.Ticker
  60. }
  61. // Check that TCPVal implements PrivValidator.
  62. var _ types.PrivValidator = (*TCPVal)(nil)
  63. // NewTCPVal returns an instance of TCPVal.
  64. func NewTCPVal(
  65. logger log.Logger,
  66. socketAddr string,
  67. privKey ed25519.PrivKeyEd25519,
  68. ) *TCPVal {
  69. sc := &TCPVal{
  70. addr: socketAddr,
  71. acceptDeadline: acceptDeadline,
  72. connTimeout: connTimeout,
  73. connHeartbeat: connHeartbeat,
  74. privKey: privKey,
  75. }
  76. sc.BaseService = *cmn.NewBaseService(logger, "TCPVal", sc)
  77. return sc
  78. }
  79. // OnStart implements cmn.Service.
  80. func (sc *TCPVal) OnStart() error {
  81. if err := sc.listen(); err != nil {
  82. sc.Logger.Error("OnStart", "err", err)
  83. return err
  84. }
  85. conn, err := sc.waitConnection()
  86. if err != nil {
  87. sc.Logger.Error("OnStart", "err", err)
  88. return err
  89. }
  90. sc.conn = conn
  91. sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn)
  92. if err != nil {
  93. return err
  94. }
  95. // Start a routine to keep the connection alive
  96. sc.cancelPing = make(chan struct{}, 1)
  97. sc.pingTicker = time.NewTicker(sc.connHeartbeat)
  98. go func() {
  99. for {
  100. select {
  101. case <-sc.pingTicker.C:
  102. err := sc.Ping()
  103. if err != nil {
  104. sc.Logger.Error(
  105. "Ping",
  106. "err", err,
  107. )
  108. }
  109. case <-sc.cancelPing:
  110. sc.pingTicker.Stop()
  111. return
  112. }
  113. }
  114. }()
  115. return nil
  116. }
  117. // OnStop implements cmn.Service.
  118. func (sc *TCPVal) OnStop() {
  119. if sc.cancelPing != nil {
  120. close(sc.cancelPing)
  121. }
  122. if sc.conn != nil {
  123. if err := sc.conn.Close(); err != nil {
  124. sc.Logger.Error("OnStop", "err", err)
  125. }
  126. }
  127. if sc.listener != nil {
  128. if err := sc.listener.Close(); err != nil {
  129. sc.Logger.Error("OnStop", "err", err)
  130. }
  131. }
  132. }
  133. func (sc *TCPVal) acceptConnection() (net.Conn, error) {
  134. conn, err := sc.listener.Accept()
  135. if err != nil {
  136. if !sc.IsRunning() {
  137. return nil, nil // Ignore error from listener closing.
  138. }
  139. return nil, err
  140. }
  141. conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey)
  142. if err != nil {
  143. return nil, err
  144. }
  145. return conn, nil
  146. }
  147. func (sc *TCPVal) listen() error {
  148. ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr))
  149. if err != nil {
  150. return err
  151. }
  152. sc.listener = newTCPTimeoutListener(
  153. ln,
  154. sc.acceptDeadline,
  155. sc.connTimeout,
  156. sc.connHeartbeat,
  157. )
  158. return nil
  159. }
  160. // waitConnection uses the configured wait timeout to error if no external
  161. // process connects in the time period.
  162. func (sc *TCPVal) waitConnection() (net.Conn, error) {
  163. var (
  164. connc = make(chan net.Conn, 1)
  165. errc = make(chan error, 1)
  166. )
  167. go func(connc chan<- net.Conn, errc chan<- error) {
  168. conn, err := sc.acceptConnection()
  169. if err != nil {
  170. errc <- err
  171. return
  172. }
  173. connc <- conn
  174. }(connc, errc)
  175. select {
  176. case conn := <-connc:
  177. return conn, nil
  178. case err := <-errc:
  179. return nil, err
  180. }
  181. }