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.

262 lines
6.5 KiB

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