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  }