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.

255 lines
6.0 KiB

  1. package privval
  2. import (
  3. "fmt"
  4. "io"
  5. "net"
  6. "sync"
  7. "github.com/tendermint/go-amino"
  8. "github.com/tendermint/tendermint/crypto"
  9. cmn "github.com/tendermint/tendermint/libs/common"
  10. "github.com/tendermint/tendermint/types"
  11. )
  12. // RemoteSignerClient implements PrivValidator, it uses a socket to request signatures
  13. // from an external process.
  14. type RemoteSignerClient struct {
  15. conn net.Conn
  16. lock sync.Mutex
  17. }
  18. // Check that RemoteSignerClient implements PrivValidator.
  19. var _ types.PrivValidator = (*RemoteSignerClient)(nil)
  20. // NewRemoteSignerClient returns an instance of RemoteSignerClient.
  21. func NewRemoteSignerClient(
  22. conn net.Conn,
  23. ) *RemoteSignerClient {
  24. sc := &RemoteSignerClient{
  25. conn: conn,
  26. }
  27. return sc
  28. }
  29. // GetAddress implements PrivValidator.
  30. func (sc *RemoteSignerClient) GetAddress() types.Address {
  31. pubKey, err := sc.getPubKey()
  32. if err != nil {
  33. panic(err)
  34. }
  35. return pubKey.Address()
  36. }
  37. // GetPubKey implements PrivValidator.
  38. func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey {
  39. pubKey, err := sc.getPubKey()
  40. if err != nil {
  41. panic(err)
  42. }
  43. return pubKey
  44. }
  45. func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) {
  46. sc.lock.Lock()
  47. defer sc.lock.Unlock()
  48. err := writeMsg(sc.conn, &PubKeyMsg{})
  49. if err != nil {
  50. return nil, err
  51. }
  52. res, err := readMsg(sc.conn)
  53. if err != nil {
  54. return nil, err
  55. }
  56. return res.(*PubKeyMsg).PubKey, nil
  57. }
  58. // SignVote implements PrivValidator.
  59. func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error {
  60. sc.lock.Lock()
  61. defer sc.lock.Unlock()
  62. err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote})
  63. if err != nil {
  64. return err
  65. }
  66. res, err := readMsg(sc.conn)
  67. if err != nil {
  68. return err
  69. }
  70. resp, ok := res.(*SignedVoteResponse)
  71. if !ok {
  72. return ErrUnexpectedResponse
  73. }
  74. if resp.Error != nil {
  75. return resp.Error
  76. }
  77. *vote = *resp.Vote
  78. return nil
  79. }
  80. // SignProposal implements PrivValidator.
  81. func (sc *RemoteSignerClient) SignProposal(
  82. chainID string,
  83. proposal *types.Proposal,
  84. ) error {
  85. sc.lock.Lock()
  86. defer sc.lock.Unlock()
  87. err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal})
  88. if err != nil {
  89. return err
  90. }
  91. res, err := readMsg(sc.conn)
  92. if err != nil {
  93. return err
  94. }
  95. resp, ok := res.(*SignedProposalResponse)
  96. if !ok {
  97. return ErrUnexpectedResponse
  98. }
  99. if resp.Error != nil {
  100. return resp.Error
  101. }
  102. *proposal = *resp.Proposal
  103. return nil
  104. }
  105. // Ping is used to check connection health.
  106. func (sc *RemoteSignerClient) Ping() error {
  107. sc.lock.Lock()
  108. defer sc.lock.Unlock()
  109. err := writeMsg(sc.conn, &PingRequest{})
  110. if err != nil {
  111. return err
  112. }
  113. res, err := readMsg(sc.conn)
  114. if err != nil {
  115. return err
  116. }
  117. _, ok := res.(*PingResponse)
  118. if !ok {
  119. return ErrUnexpectedResponse
  120. }
  121. return nil
  122. }
  123. // RemoteSignerMsg is sent between RemoteSigner and the RemoteSigner client.
  124. type RemoteSignerMsg interface{}
  125. func RegisterRemoteSignerMsg(cdc *amino.Codec) {
  126. cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil)
  127. cdc.RegisterConcrete(&PubKeyMsg{}, "tendermint/remotesigner/PubKeyMsg", nil)
  128. cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil)
  129. cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil)
  130. cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil)
  131. cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/remotesigner/SignedProposalResponse", nil)
  132. cdc.RegisterConcrete(&PingRequest{}, "tendermint/remotesigner/PingRequest", nil)
  133. cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil)
  134. }
  135. // PubKeyMsg is a PrivValidatorSocket message containing the public key.
  136. type PubKeyMsg struct {
  137. PubKey crypto.PubKey
  138. }
  139. // SignVoteRequest is a PrivValidatorSocket message containing a vote.
  140. type SignVoteRequest struct {
  141. Vote *types.Vote
  142. }
  143. // SignedVoteResponse is a PrivValidatorSocket message containing a signed vote along with a potenial error message.
  144. type SignedVoteResponse struct {
  145. Vote *types.Vote
  146. Error *RemoteSignerError
  147. }
  148. // SignProposalRequest is a PrivValidatorSocket message containing a Proposal.
  149. type SignProposalRequest struct {
  150. Proposal *types.Proposal
  151. }
  152. type SignedProposalResponse struct {
  153. Proposal *types.Proposal
  154. Error *RemoteSignerError
  155. }
  156. // PingRequest is a PrivValidatorSocket message to keep the connection alive.
  157. type PingRequest struct {
  158. }
  159. type PingResponse struct {
  160. }
  161. // RemoteSignerError allows (remote) validators to include meaningful error descriptions in their reply.
  162. type RemoteSignerError struct {
  163. // TODO(ismail): create an enum of known errors
  164. Code int
  165. Description string
  166. }
  167. func (e *RemoteSignerError) Error() string {
  168. return fmt.Sprintf("RemoteSigner returned error #%d: %s", e.Code, e.Description)
  169. }
  170. func readMsg(r io.Reader) (msg RemoteSignerMsg, err error) {
  171. const maxRemoteSignerMsgSize = 1024 * 10
  172. _, err = cdc.UnmarshalBinaryLengthPrefixedReader(r, &msg, maxRemoteSignerMsgSize)
  173. if _, ok := err.(timeoutError); ok {
  174. err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
  175. }
  176. return
  177. }
  178. func writeMsg(w io.Writer, msg interface{}) (err error) {
  179. _, err = cdc.MarshalBinaryLengthPrefixedWriter(w, msg)
  180. if _, ok := err.(timeoutError); ok {
  181. err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
  182. }
  183. return
  184. }
  185. func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValidator) (RemoteSignerMsg, error) {
  186. var res RemoteSignerMsg
  187. var err error
  188. switch r := req.(type) {
  189. case *PubKeyMsg:
  190. var p crypto.PubKey
  191. p = privVal.GetPubKey()
  192. res = &PubKeyMsg{p}
  193. case *SignVoteRequest:
  194. err = privVal.SignVote(chainID, r.Vote)
  195. if err != nil {
  196. res = &SignedVoteResponse{nil, &RemoteSignerError{0, err.Error()}}
  197. } else {
  198. res = &SignedVoteResponse{r.Vote, nil}
  199. }
  200. case *SignProposalRequest:
  201. err = privVal.SignProposal(chainID, r.Proposal)
  202. if err != nil {
  203. res = &SignedProposalResponse{nil, &RemoteSignerError{0, err.Error()}}
  204. } else {
  205. res = &SignedProposalResponse{r.Proposal, nil}
  206. }
  207. case *PingRequest:
  208. res = &PingResponse{}
  209. default:
  210. err = fmt.Errorf("unknown msg: %v", r)
  211. }
  212. return res, err
  213. }