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

     1  package privval
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/evdatsion/aphelion-dpos-bft/crypto"
    10  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    11  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    12  	"github.com/evdatsion/aphelion-dpos-bft/types"
    13  )
    14  
    15  const (
    16  	defaultHeartbeatSeconds = 2
    17  	defaultMaxDialRetries   = 10
    18  )
    19  
    20  var (
    21  	heartbeatPeriod = time.Second * defaultHeartbeatSeconds
    22  )
    23  
    24  // SignerValidatorEndpointOption sets an optional parameter on the SocketVal.
    25  type SignerValidatorEndpointOption func(*SignerValidatorEndpoint)
    26  
    27  // SignerValidatorEndpointSetHeartbeat sets the period on which to check the liveness of the
    28  // connected Signer connections.
    29  func SignerValidatorEndpointSetHeartbeat(period time.Duration) SignerValidatorEndpointOption {
    30  	return func(sc *SignerValidatorEndpoint) { sc.heartbeatPeriod = period }
    31  }
    32  
    33  // SocketVal implements PrivValidator.
    34  // It listens for an external process to dial in and uses
    35  // the socket to request signatures.
    36  type SignerValidatorEndpoint struct {
    37  	cmn.BaseService
    38  
    39  	listener net.Listener
    40  
    41  	// ping
    42  	cancelPingCh    chan struct{}
    43  	pingTicker      *time.Ticker
    44  	heartbeatPeriod time.Duration
    45  
    46  	// signer is mutable since it can be reset if the connection fails.
    47  	// failures are detected by a background ping routine.
    48  	// All messages are request/response, so we hold the mutex
    49  	// so only one request/response pair can happen at a time.
    50  	// Methods on the underlying net.Conn itself are already goroutine safe.
    51  	mtx sync.Mutex
    52  
    53  	// TODO: Signer should encapsulate and hide the endpoint completely. Invert the relation
    54  	signer *SignerRemote
    55  }
    56  
    57  // Check that SignerValidatorEndpoint implements PrivValidator.
    58  var _ types.PrivValidator = (*SignerValidatorEndpoint)(nil)
    59  
    60  // NewSignerValidatorEndpoint returns an instance of SignerValidatorEndpoint.
    61  func NewSignerValidatorEndpoint(logger log.Logger, listener net.Listener) *SignerValidatorEndpoint {
    62  	sc := &SignerValidatorEndpoint{
    63  		listener:        listener,
    64  		heartbeatPeriod: heartbeatPeriod,
    65  	}
    66  
    67  	sc.BaseService = *cmn.NewBaseService(logger, "SignerValidatorEndpoint", sc)
    68  
    69  	return sc
    70  }
    71  
    72  //--------------------------------------------------------
    73  // Implement PrivValidator
    74  
    75  // GetPubKey implements PrivValidator.
    76  func (ve *SignerValidatorEndpoint) GetPubKey() crypto.PubKey {
    77  	ve.mtx.Lock()
    78  	defer ve.mtx.Unlock()
    79  	return ve.signer.GetPubKey()
    80  }
    81  
    82  // SignVote implements PrivValidator.
    83  func (ve *SignerValidatorEndpoint) SignVote(chainID string, vote *types.Vote) error {
    84  	ve.mtx.Lock()
    85  	defer ve.mtx.Unlock()
    86  	return ve.signer.SignVote(chainID, vote)
    87  }
    88  
    89  // SignProposal implements PrivValidator.
    90  func (ve *SignerValidatorEndpoint) SignProposal(chainID string, proposal *types.Proposal) error {
    91  	ve.mtx.Lock()
    92  	defer ve.mtx.Unlock()
    93  	return ve.signer.SignProposal(chainID, proposal)
    94  }
    95  
    96  //--------------------------------------------------------
    97  // More thread safe methods proxied to the signer
    98  
    99  // Ping is used to check connection health.
   100  func (ve *SignerValidatorEndpoint) Ping() error {
   101  	ve.mtx.Lock()
   102  	defer ve.mtx.Unlock()
   103  	return ve.signer.Ping()
   104  }
   105  
   106  // Close closes the underlying net.Conn.
   107  func (ve *SignerValidatorEndpoint) Close() {
   108  	ve.mtx.Lock()
   109  	defer ve.mtx.Unlock()
   110  	if ve.signer != nil {
   111  		if err := ve.signer.Close(); err != nil {
   112  			ve.Logger.Error("OnStop", "err", err)
   113  		}
   114  	}
   115  
   116  	if ve.listener != nil {
   117  		if err := ve.listener.Close(); err != nil {
   118  			ve.Logger.Error("OnStop", "err", err)
   119  		}
   120  	}
   121  }
   122  
   123  //--------------------------------------------------------
   124  // Service start and stop
   125  
   126  // OnStart implements cmn.Service.
   127  func (ve *SignerValidatorEndpoint) OnStart() error {
   128  	if closed, err := ve.reset(); err != nil {
   129  		ve.Logger.Error("OnStart", "err", err)
   130  		return err
   131  	} else if closed {
   132  		return fmt.Errorf("listener is closed")
   133  	}
   134  
   135  	// Start a routine to keep the connection alive
   136  	ve.cancelPingCh = make(chan struct{}, 1)
   137  	ve.pingTicker = time.NewTicker(ve.heartbeatPeriod)
   138  	go func() {
   139  		for {
   140  			select {
   141  			case <-ve.pingTicker.C:
   142  				err := ve.Ping()
   143  				if err != nil {
   144  					ve.Logger.Error("Ping", "err", err)
   145  					if err == ErrUnexpectedResponse {
   146  						return
   147  					}
   148  
   149  					closed, err := ve.reset()
   150  					if err != nil {
   151  						ve.Logger.Error("Reconnecting to remote signer failed", "err", err)
   152  						continue
   153  					}
   154  					if closed {
   155  						ve.Logger.Info("listener is closing")
   156  						return
   157  					}
   158  
   159  					ve.Logger.Info("Re-created connection to remote signer", "impl", ve)
   160  				}
   161  			case <-ve.cancelPingCh:
   162  				ve.pingTicker.Stop()
   163  				return
   164  			}
   165  		}
   166  	}()
   167  
   168  	return nil
   169  }
   170  
   171  // OnStop implements cmn.Service.
   172  func (ve *SignerValidatorEndpoint) OnStop() {
   173  	if ve.cancelPingCh != nil {
   174  		close(ve.cancelPingCh)
   175  	}
   176  	ve.Close()
   177  }
   178  
   179  //--------------------------------------------------------
   180  // Connection and signer management
   181  
   182  // waits to accept and sets a new connection.
   183  // connection is closed in OnStop.
   184  // returns true if the listener is closed
   185  // (ie. it returns a nil conn).
   186  func (ve *SignerValidatorEndpoint) reset() (closed bool, err error) {
   187  	ve.mtx.Lock()
   188  	defer ve.mtx.Unlock()
   189  
   190  	// first check if the conn already exists and close it.
   191  	if ve.signer != nil {
   192  		if tmpErr := ve.signer.Close(); tmpErr != nil {
   193  			ve.Logger.Error("error closing socket val connection during reset", "err", tmpErr)
   194  		}
   195  	}
   196  
   197  	// wait for a new conn
   198  	conn, err := ve.acceptConnection()
   199  	if err != nil {
   200  		return false, err
   201  	}
   202  
   203  	// listener is closed
   204  	if conn == nil {
   205  		return true, nil
   206  	}
   207  
   208  	ve.signer, err = NewSignerRemote(conn)
   209  	if err != nil {
   210  		// failed to fetch the pubkey. close out the connection.
   211  		if tmpErr := conn.Close(); tmpErr != nil {
   212  			ve.Logger.Error("error closing connection", "err", tmpErr)
   213  		}
   214  		return false, err
   215  	}
   216  	return false, nil
   217  }
   218  
   219  // Attempt to accept a connection.
   220  // Times out after the listener's timeoutAccept
   221  func (ve *SignerValidatorEndpoint) acceptConnection() (net.Conn, error) {
   222  	conn, err := ve.listener.Accept()
   223  	if err != nil {
   224  		if !ve.IsRunning() {
   225  			return nil, nil // Ignore error from listener closing.
   226  		}
   227  		return nil, err
   228  	}
   229  	return conn, nil
   230  }