github.com/evdatsion/aphelion-dpos-bft@v0.32.1/privval/signer_remote.go (about) 1 package privval 2 3 import ( 4 "fmt" 5 "io" 6 "net" 7 8 "github.com/pkg/errors" 9 10 "github.com/evdatsion/aphelion-dpos-bft/crypto" 11 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 12 "github.com/evdatsion/aphelion-dpos-bft/types" 13 ) 14 15 // SignerRemote implements PrivValidator. 16 // It uses a net.Conn to request signatures from an external process. 17 type SignerRemote struct { 18 conn net.Conn 19 20 // memoized 21 consensusPubKey crypto.PubKey 22 } 23 24 // Check that SignerRemote implements PrivValidator. 25 var _ types.PrivValidator = (*SignerRemote)(nil) 26 27 // NewSignerRemote returns an instance of SignerRemote. 28 func NewSignerRemote(conn net.Conn) (*SignerRemote, error) { 29 30 // retrieve and memoize the consensus public key once. 31 pubKey, err := getPubKey(conn) 32 if err != nil { 33 return nil, cmn.ErrorWrap(err, "error while retrieving public key for remote signer") 34 } 35 return &SignerRemote{ 36 conn: conn, 37 consensusPubKey: pubKey, 38 }, nil 39 } 40 41 // Close calls Close on the underlying net.Conn. 42 func (sc *SignerRemote) Close() error { 43 return sc.conn.Close() 44 } 45 46 // GetPubKey implements PrivValidator. 47 func (sc *SignerRemote) GetPubKey() crypto.PubKey { 48 return sc.consensusPubKey 49 } 50 51 // not thread-safe (only called on startup). 52 func getPubKey(conn net.Conn) (crypto.PubKey, error) { 53 err := writeMsg(conn, &PubKeyRequest{}) 54 if err != nil { 55 return nil, err 56 } 57 58 res, err := readMsg(conn) 59 if err != nil { 60 return nil, err 61 } 62 63 pubKeyResp, ok := res.(*PubKeyResponse) 64 if !ok { 65 return nil, errors.Wrap(ErrUnexpectedResponse, "response is not PubKeyResponse") 66 } 67 68 if pubKeyResp.Error != nil { 69 return nil, errors.Wrap(pubKeyResp.Error, "failed to get private validator's public key") 70 } 71 72 return pubKeyResp.PubKey, nil 73 } 74 75 // SignVote implements PrivValidator. 76 func (sc *SignerRemote) SignVote(chainID string, vote *types.Vote) error { 77 err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote}) 78 if err != nil { 79 return err 80 } 81 82 res, err := readMsg(sc.conn) 83 if err != nil { 84 return err 85 } 86 87 resp, ok := res.(*SignedVoteResponse) 88 if !ok { 89 return ErrUnexpectedResponse 90 } 91 if resp.Error != nil { 92 return resp.Error 93 } 94 *vote = *resp.Vote 95 96 return nil 97 } 98 99 // SignProposal implements PrivValidator. 100 func (sc *SignerRemote) SignProposal(chainID string, proposal *types.Proposal) error { 101 err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal}) 102 if err != nil { 103 return err 104 } 105 106 res, err := readMsg(sc.conn) 107 if err != nil { 108 return err 109 } 110 resp, ok := res.(*SignedProposalResponse) 111 if !ok { 112 return ErrUnexpectedResponse 113 } 114 if resp.Error != nil { 115 return resp.Error 116 } 117 *proposal = *resp.Proposal 118 119 return nil 120 } 121 122 // Ping is used to check connection health. 123 func (sc *SignerRemote) Ping() error { 124 err := writeMsg(sc.conn, &PingRequest{}) 125 if err != nil { 126 return err 127 } 128 129 res, err := readMsg(sc.conn) 130 if err != nil { 131 return err 132 } 133 _, ok := res.(*PingResponse) 134 if !ok { 135 return ErrUnexpectedResponse 136 } 137 138 return nil 139 } 140 141 func readMsg(r io.Reader) (msg RemoteSignerMsg, err error) { 142 const maxRemoteSignerMsgSize = 1024 * 10 143 _, err = cdc.UnmarshalBinaryLengthPrefixedReader(r, &msg, maxRemoteSignerMsgSize) 144 if _, ok := err.(timeoutError); ok { 145 err = cmn.ErrorWrap(ErrConnTimeout, err.Error()) 146 } 147 return 148 } 149 150 func writeMsg(w io.Writer, msg interface{}) (err error) { 151 _, err = cdc.MarshalBinaryLengthPrefixedWriter(w, msg) 152 if _, ok := err.(timeoutError); ok { 153 err = cmn.ErrorWrap(ErrConnTimeout, err.Error()) 154 } 155 return 156 } 157 158 func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValidator) (RemoteSignerMsg, error) { 159 var res RemoteSignerMsg 160 var err error 161 162 switch r := req.(type) { 163 case *PubKeyRequest: 164 var p crypto.PubKey 165 p = privVal.GetPubKey() 166 res = &PubKeyResponse{p, nil} 167 168 case *SignVoteRequest: 169 err = privVal.SignVote(chainID, r.Vote) 170 if err != nil { 171 res = &SignedVoteResponse{nil, &RemoteSignerError{0, err.Error()}} 172 } else { 173 res = &SignedVoteResponse{r.Vote, nil} 174 } 175 176 case *SignProposalRequest: 177 err = privVal.SignProposal(chainID, r.Proposal) 178 if err != nil { 179 res = &SignedProposalResponse{nil, &RemoteSignerError{0, err.Error()}} 180 } else { 181 res = &SignedProposalResponse{r.Proposal, nil} 182 } 183 184 case *PingRequest: 185 res = &PingResponse{} 186 187 default: 188 err = fmt.Errorf("unknown msg: %v", r) 189 } 190 191 return res, err 192 }