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.

214 lines
4.6 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 = NewRemoteSignerClient(sc.conn)
  92. // Start a routine to keep the connection alive
  93. sc.cancelPing = make(chan struct{}, 1)
  94. sc.pingTicker = time.NewTicker(sc.connHeartbeat)
  95. go func() {
  96. for {
  97. select {
  98. case <-sc.pingTicker.C:
  99. err := sc.Ping()
  100. if err != nil {
  101. sc.Logger.Error(
  102. "Ping",
  103. "err", err,
  104. )
  105. }
  106. case <-sc.cancelPing:
  107. sc.pingTicker.Stop()
  108. return
  109. }
  110. }
  111. }()
  112. return nil
  113. }
  114. // OnStop implements cmn.Service.
  115. func (sc *TCPVal) OnStop() {
  116. if sc.cancelPing != nil {
  117. close(sc.cancelPing)
  118. }
  119. if sc.conn != nil {
  120. if err := sc.conn.Close(); err != nil {
  121. sc.Logger.Error("OnStop", "err", err)
  122. }
  123. }
  124. if sc.listener != nil {
  125. if err := sc.listener.Close(); err != nil {
  126. sc.Logger.Error("OnStop", "err", err)
  127. }
  128. }
  129. }
  130. func (sc *TCPVal) acceptConnection() (net.Conn, error) {
  131. conn, err := sc.listener.Accept()
  132. if err != nil {
  133. if !sc.IsRunning() {
  134. return nil, nil // Ignore error from listener closing.
  135. }
  136. return nil, err
  137. }
  138. conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return conn, nil
  143. }
  144. func (sc *TCPVal) listen() error {
  145. ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr))
  146. if err != nil {
  147. return err
  148. }
  149. sc.listener = newTCPTimeoutListener(
  150. ln,
  151. sc.acceptDeadline,
  152. sc.connTimeout,
  153. sc.connHeartbeat,
  154. )
  155. return nil
  156. }
  157. // waitConnection uses the configured wait timeout to error if no external
  158. // process connects in the time period.
  159. func (sc *TCPVal) waitConnection() (net.Conn, error) {
  160. var (
  161. connc = make(chan net.Conn, 1)
  162. errc = make(chan error, 1)
  163. )
  164. go func(connc chan<- net.Conn, errc chan<- error) {
  165. conn, err := sc.acceptConnection()
  166. if err != nil {
  167. errc <- err
  168. return
  169. }
  170. connc <- conn
  171. }(connc, errc)
  172. select {
  173. case conn := <-connc:
  174. return conn, nil
  175. case err := <-errc:
  176. return nil, err
  177. }
  178. }