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.

120 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 = NewRemoteSignerClient(sc.conn)
  56. // Start a routine to keep the connection alive
  57. sc.cancelPing = make(chan struct{}, 1)
  58. sc.pingTicker = time.NewTicker(sc.connHeartbeat)
  59. go func() {
  60. for {
  61. select {
  62. case <-sc.pingTicker.C:
  63. err := sc.Ping()
  64. if err != nil {
  65. sc.Logger.Error("Ping", "err", err)
  66. }
  67. case <-sc.cancelPing:
  68. sc.pingTicker.Stop()
  69. return
  70. }
  71. }
  72. }()
  73. return nil
  74. }
  75. // OnStop implements cmn.Service.
  76. func (sc *IPCVal) OnStop() {
  77. if sc.cancelPing != nil {
  78. close(sc.cancelPing)
  79. }
  80. if sc.conn != nil {
  81. if err := sc.conn.Close(); err != nil {
  82. sc.Logger.Error("OnStop", "err", err)
  83. }
  84. }
  85. }
  86. func (sc *IPCVal) connect() error {
  87. la, err := net.ResolveUnixAddr("unix", sc.addr)
  88. if err != nil {
  89. return err
  90. }
  91. conn, err := net.DialUnix("unix", nil, la)
  92. if err != nil {
  93. return err
  94. }
  95. sc.conn = newTimeoutConn(conn, sc.connTimeout)
  96. return nil
  97. }