github.com/evdatsion/aphelion-dpos-bft@v0.32.1/privval/signer_service_endpoint.go (about)

     1  package privval
     2  
     3  import (
     4  	"io"
     5  	"net"
     6  	"time"
     7  
     8  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
     9  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    10  	"github.com/evdatsion/aphelion-dpos-bft/types"
    11  )
    12  
    13  // SignerServiceEndpointOption sets an optional parameter on the SignerServiceEndpoint.
    14  type SignerServiceEndpointOption func(*SignerServiceEndpoint)
    15  
    16  // SignerServiceEndpointTimeoutReadWrite sets the read and write timeout for connections
    17  // from external signing processes.
    18  func SignerServiceEndpointTimeoutReadWrite(timeout time.Duration) SignerServiceEndpointOption {
    19  	return func(ss *SignerServiceEndpoint) { ss.timeoutReadWrite = timeout }
    20  }
    21  
    22  // SignerServiceEndpointConnRetries sets the amount of attempted retries to connect.
    23  func SignerServiceEndpointConnRetries(retries int) SignerServiceEndpointOption {
    24  	return func(ss *SignerServiceEndpoint) { ss.connRetries = retries }
    25  }
    26  
    27  // SignerServiceEndpoint dials using its dialer and responds to any
    28  // signature requests using its privVal.
    29  type SignerServiceEndpoint struct {
    30  	cmn.BaseService
    31  
    32  	chainID          string
    33  	timeoutReadWrite time.Duration
    34  	connRetries      int
    35  	privVal          types.PrivValidator
    36  
    37  	dialer SocketDialer
    38  	conn   net.Conn
    39  }
    40  
    41  // NewSignerServiceEndpoint returns a SignerServiceEndpoint that will dial using the given
    42  // dialer and respond to any signature requests over the connection
    43  // using the given privVal.
    44  func NewSignerServiceEndpoint(
    45  	logger log.Logger,
    46  	chainID string,
    47  	privVal types.PrivValidator,
    48  	dialer SocketDialer,
    49  ) *SignerServiceEndpoint {
    50  	se := &SignerServiceEndpoint{
    51  		chainID:          chainID,
    52  		timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds,
    53  		connRetries:      defaultMaxDialRetries,
    54  		privVal:          privVal,
    55  		dialer:           dialer,
    56  	}
    57  
    58  	se.BaseService = *cmn.NewBaseService(logger, "SignerServiceEndpoint", se)
    59  	return se
    60  }
    61  
    62  // OnStart implements cmn.Service.
    63  func (se *SignerServiceEndpoint) OnStart() error {
    64  	conn, err := se.connect()
    65  	if err != nil {
    66  		se.Logger.Error("OnStart", "err", err)
    67  		return err
    68  	}
    69  
    70  	se.conn = conn
    71  	go se.handleConnection(conn)
    72  
    73  	return nil
    74  }
    75  
    76  // OnStop implements cmn.Service.
    77  func (se *SignerServiceEndpoint) OnStop() {
    78  	if se.conn == nil {
    79  		return
    80  	}
    81  
    82  	if err := se.conn.Close(); err != nil {
    83  		se.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
    84  	}
    85  }
    86  
    87  func (se *SignerServiceEndpoint) connect() (net.Conn, error) {
    88  	for retries := 0; retries < se.connRetries; retries++ {
    89  		// Don't sleep if it is the first retry.
    90  		if retries > 0 {
    91  			time.Sleep(se.timeoutReadWrite)
    92  		}
    93  
    94  		conn, err := se.dialer()
    95  		if err == nil {
    96  			return conn, nil
    97  		}
    98  
    99  		se.Logger.Error("dialing", "err", err)
   100  	}
   101  
   102  	return nil, ErrDialRetryMax
   103  }
   104  
   105  func (se *SignerServiceEndpoint) handleConnection(conn net.Conn) {
   106  	for {
   107  		if !se.IsRunning() {
   108  			return // Ignore error from listener closing.
   109  		}
   110  
   111  		// Reset the connection deadline
   112  		deadline := time.Now().Add(se.timeoutReadWrite)
   113  		err := conn.SetDeadline(deadline)
   114  		if err != nil {
   115  			return
   116  		}
   117  
   118  		req, err := readMsg(conn)
   119  		if err != nil {
   120  			if err != io.EOF {
   121  				se.Logger.Error("handleConnection readMsg", "err", err)
   122  			}
   123  			return
   124  		}
   125  
   126  		res, err := handleRequest(req, se.chainID, se.privVal)
   127  
   128  		if err != nil {
   129  			// only log the error; we'll reply with an error in res
   130  			se.Logger.Error("handleConnection handleRequest", "err", err)
   131  		}
   132  
   133  		err = writeMsg(conn, res)
   134  		if err != nil {
   135  			se.Logger.Error("handleConnection writeMsg", "err", err)
   136  			return
   137  		}
   138  	}
   139  }