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.

123 lines
2.5 KiB

  1. package privval
  2. import (
  3. "net"
  4. "time"
  5. cmn "github.com/tendermint/tendermint/libs/common"
  6. "github.com/tendermint/tendermint/libs/log"
  7. "github.com/tendermint/tendermint/types"
  8. )
  9. // IPCValOption sets an optional parameter on the SocketPV.
  10. type IPCValOption func(*IPCVal)
  11. // IPCValConnTimeout sets the read and write timeout for connections
  12. // from external signing processes.
  13. func IPCValConnTimeout(timeout time.Duration) IPCValOption {
  14. return func(sc *IPCVal) { sc.connTimeout = timeout }
  15. }
  16. // IPCValHeartbeat sets the period on which to check the liveness of the
  17. // connected Signer connections.
  18. func IPCValHeartbeat(period time.Duration) IPCValOption {
  19. return func(sc *IPCVal) { sc.connHeartbeat = period }
  20. }
  21. // IPCVal implements PrivValidator, it uses a unix socket to request signatures
  22. // from an external process.
  23. type IPCVal struct {
  24. cmn.BaseService
  25. *RemoteSignerClient
  26. addr string
  27. connTimeout time.Duration
  28. connHeartbeat time.Duration
  29. conn net.Conn
  30. cancelPing chan struct{}
  31. pingTicker *time.Ticker
  32. }
  33. // Check that IPCVal implements PrivValidator.
  34. var _ types.PrivValidator = (*IPCVal)(nil)
  35. // NewIPCVal returns an instance of IPCVal.
  36. func NewIPCVal(
  37. logger log.Logger,
  38. socketAddr string,
  39. ) *IPCVal {
  40. sc := &IPCVal{
  41. addr: socketAddr,
  42. connTimeout: connTimeout,
  43. connHeartbeat: connHeartbeat,
  44. }
  45. sc.BaseService = *cmn.NewBaseService(logger, "IPCVal", sc)
  46. return sc
  47. }
  48. // OnStart implements cmn.Service.
  49. func (sc *IPCVal) OnStart() error {
  50. err := sc.connect()
  51. if err != nil {
  52. sc.Logger.Error("OnStart", "err", err)
  53. return err
  54. }
  55. sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn)
  56. if err != nil {
  57. return err
  58. }
  59. // Start a routine to keep the connection alive
  60. sc.cancelPing = make(chan struct{}, 1)
  61. sc.pingTicker = time.NewTicker(sc.connHeartbeat)
  62. go func() {
  63. for {
  64. select {
  65. case <-sc.pingTicker.C:
  66. err := sc.Ping()
  67. if err != nil {
  68. sc.Logger.Error("Ping", "err", err)
  69. }
  70. case <-sc.cancelPing:
  71. sc.pingTicker.Stop()
  72. return
  73. }
  74. }
  75. }()
  76. return nil
  77. }
  78. // OnStop implements cmn.Service.
  79. func (sc *IPCVal) OnStop() {
  80. if sc.cancelPing != nil {
  81. close(sc.cancelPing)
  82. }
  83. if sc.conn != nil {
  84. if err := sc.conn.Close(); err != nil {
  85. sc.Logger.Error("OnStop", "err", err)
  86. }
  87. }
  88. }
  89. func (sc *IPCVal) connect() error {
  90. la, err := net.ResolveUnixAddr("unix", sc.addr)
  91. if err != nil {
  92. return err
  93. }
  94. conn, err := net.DialUnix("unix", nil, la)
  95. if err != nil {
  96. return err
  97. }
  98. sc.conn = newTimeoutConn(conn, sc.connTimeout)
  99. return nil
  100. }