|
|
- package privval
-
- import (
- "errors"
- "net"
- "time"
-
- "github.com/tendermint/tendermint/crypto/ed25519"
- cmn "github.com/tendermint/tendermint/libs/common"
- "github.com/tendermint/tendermint/libs/log"
- p2pconn "github.com/tendermint/tendermint/p2p/conn"
- "github.com/tendermint/tendermint/types"
- )
-
- const (
- defaultAcceptDeadlineSeconds = 3
- defaultConnDeadlineSeconds = 3
- defaultConnHeartBeatSeconds = 2
- defaultDialRetries = 10
- )
-
- // Socket errors.
- var (
- ErrDialRetryMax = errors.New("dialed maximum retries")
- ErrConnTimeout = errors.New("remote signer timed out")
- ErrUnexpectedResponse = errors.New("received unexpected response")
- )
-
- var (
- acceptDeadline = time.Second * defaultAcceptDeadlineSeconds
- connTimeout = time.Second * defaultConnDeadlineSeconds
- connHeartbeat = time.Second * defaultConnHeartBeatSeconds
- )
-
- // TCPValOption sets an optional parameter on the SocketPV.
- type TCPValOption func(*TCPVal)
-
- // TCPValAcceptDeadline sets the deadline for the TCPVal listener.
- // A zero time value disables the deadline.
- func TCPValAcceptDeadline(deadline time.Duration) TCPValOption {
- return func(sc *TCPVal) { sc.acceptDeadline = deadline }
- }
-
- // TCPValConnTimeout sets the read and write timeout for connections
- // from external signing processes.
- func TCPValConnTimeout(timeout time.Duration) TCPValOption {
- return func(sc *TCPVal) { sc.connTimeout = timeout }
- }
-
- // TCPValHeartbeat sets the period on which to check the liveness of the
- // connected Signer connections.
- func TCPValHeartbeat(period time.Duration) TCPValOption {
- return func(sc *TCPVal) { sc.connHeartbeat = period }
- }
-
- // TCPVal implements PrivValidator, it uses a socket to request signatures
- // from an external process.
- type TCPVal struct {
- cmn.BaseService
- *RemoteSignerClient
-
- addr string
- acceptDeadline time.Duration
- connTimeout time.Duration
- connHeartbeat time.Duration
- privKey ed25519.PrivKeyEd25519
-
- conn net.Conn
- listener net.Listener
- cancelPing chan struct{}
- pingTicker *time.Ticker
- }
-
- // Check that TCPVal implements PrivValidator.
- var _ types.PrivValidator = (*TCPVal)(nil)
-
- // NewTCPVal returns an instance of TCPVal.
- func NewTCPVal(
- logger log.Logger,
- socketAddr string,
- privKey ed25519.PrivKeyEd25519,
- ) *TCPVal {
- sc := &TCPVal{
- addr: socketAddr,
- acceptDeadline: acceptDeadline,
- connTimeout: connTimeout,
- connHeartbeat: connHeartbeat,
- privKey: privKey,
- }
-
- sc.BaseService = *cmn.NewBaseService(logger, "TCPVal", sc)
-
- return sc
- }
-
- // OnStart implements cmn.Service.
- func (sc *TCPVal) OnStart() error {
- if err := sc.listen(); err != nil {
- sc.Logger.Error("OnStart", "err", err)
- return err
- }
-
- conn, err := sc.waitConnection()
- if err != nil {
- sc.Logger.Error("OnStart", "err", err)
- return err
- }
-
- sc.conn = conn
- sc.RemoteSignerClient, err = NewRemoteSignerClient(sc.conn)
- if err != nil {
- return err
- }
-
- // Start a routine to keep the connection alive
- sc.cancelPing = make(chan struct{}, 1)
- sc.pingTicker = time.NewTicker(sc.connHeartbeat)
- go func() {
- for {
- select {
- case <-sc.pingTicker.C:
- err := sc.Ping()
- if err != nil {
- sc.Logger.Error(
- "Ping",
- "err", err,
- )
- }
- case <-sc.cancelPing:
- sc.pingTicker.Stop()
- return
- }
- }
- }()
-
- return nil
- }
-
- // OnStop implements cmn.Service.
- func (sc *TCPVal) OnStop() {
- if sc.cancelPing != nil {
- close(sc.cancelPing)
- }
-
- if sc.conn != nil {
- if err := sc.conn.Close(); err != nil {
- sc.Logger.Error("OnStop", "err", err)
- }
- }
-
- if sc.listener != nil {
- if err := sc.listener.Close(); err != nil {
- sc.Logger.Error("OnStop", "err", err)
- }
- }
- }
-
- func (sc *TCPVal) acceptConnection() (net.Conn, error) {
- conn, err := sc.listener.Accept()
- if err != nil {
- if !sc.IsRunning() {
- return nil, nil // Ignore error from listener closing.
- }
- return nil, err
-
- }
-
- conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey)
- if err != nil {
- return nil, err
- }
-
- return conn, nil
- }
-
- func (sc *TCPVal) listen() error {
- ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr))
- if err != nil {
- return err
- }
-
- sc.listener = newTCPTimeoutListener(
- ln,
- sc.acceptDeadline,
- sc.connTimeout,
- sc.connHeartbeat,
- )
-
- return nil
- }
-
- // waitConnection uses the configured wait timeout to error if no external
- // process connects in the time period.
- func (sc *TCPVal) waitConnection() (net.Conn, error) {
- var (
- connc = make(chan net.Conn, 1)
- errc = make(chan error, 1)
- )
-
- go func(connc chan<- net.Conn, errc chan<- error) {
- conn, err := sc.acceptConnection()
- if err != nil {
- errc <- err
- return
- }
-
- connc <- conn
- }(connc, errc)
-
- select {
- case conn := <-connc:
- return conn, nil
- case err := <-errc:
- return nil, err
- }
- }
|