package privval
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"time"
|
|
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
// SignerServiceEndpointOption sets an optional parameter on the SignerServiceEndpoint.
|
|
type SignerServiceEndpointOption func(*SignerServiceEndpoint)
|
|
|
|
// SignerServiceEndpointTimeoutReadWrite sets the read and write timeout for connections
|
|
// from external signing processes.
|
|
func SignerServiceEndpointTimeoutReadWrite(timeout time.Duration) SignerServiceEndpointOption {
|
|
return func(ss *SignerServiceEndpoint) { ss.timeoutReadWrite = timeout }
|
|
}
|
|
|
|
// SignerServiceEndpointConnRetries sets the amount of attempted retries to connect.
|
|
func SignerServiceEndpointConnRetries(retries int) SignerServiceEndpointOption {
|
|
return func(ss *SignerServiceEndpoint) { ss.connRetries = retries }
|
|
}
|
|
|
|
// SignerServiceEndpoint dials using its dialer and responds to any
|
|
// signature requests using its privVal.
|
|
type SignerServiceEndpoint struct {
|
|
cmn.BaseService
|
|
|
|
chainID string
|
|
timeoutReadWrite time.Duration
|
|
connRetries int
|
|
privVal types.PrivValidator
|
|
|
|
dialer SocketDialer
|
|
conn net.Conn
|
|
}
|
|
|
|
// NewSignerServiceEndpoint returns a SignerServiceEndpoint that will dial using the given
|
|
// dialer and respond to any signature requests over the connection
|
|
// using the given privVal.
|
|
func NewSignerServiceEndpoint(
|
|
logger log.Logger,
|
|
chainID string,
|
|
privVal types.PrivValidator,
|
|
dialer SocketDialer,
|
|
) *SignerServiceEndpoint {
|
|
se := &SignerServiceEndpoint{
|
|
chainID: chainID,
|
|
timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds,
|
|
connRetries: defaultMaxDialRetries,
|
|
privVal: privVal,
|
|
dialer: dialer,
|
|
}
|
|
|
|
se.BaseService = *cmn.NewBaseService(logger, "SignerServiceEndpoint", se)
|
|
return se
|
|
}
|
|
|
|
// OnStart implements cmn.Service.
|
|
func (se *SignerServiceEndpoint) OnStart() error {
|
|
conn, err := se.connect()
|
|
if err != nil {
|
|
se.Logger.Error("OnStart", "err", err)
|
|
return err
|
|
}
|
|
|
|
se.conn = conn
|
|
go se.handleConnection(conn)
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnStop implements cmn.Service.
|
|
func (se *SignerServiceEndpoint) OnStop() {
|
|
if se.conn == nil {
|
|
return
|
|
}
|
|
|
|
if err := se.conn.Close(); err != nil {
|
|
se.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
|
|
}
|
|
}
|
|
|
|
func (se *SignerServiceEndpoint) connect() (net.Conn, error) {
|
|
for retries := 0; retries < se.connRetries; retries++ {
|
|
// Don't sleep if it is the first retry.
|
|
if retries > 0 {
|
|
time.Sleep(se.timeoutReadWrite)
|
|
}
|
|
|
|
conn, err := se.dialer()
|
|
if err == nil {
|
|
return conn, nil
|
|
}
|
|
|
|
se.Logger.Error("dialing", "err", err)
|
|
}
|
|
|
|
return nil, ErrDialRetryMax
|
|
}
|
|
|
|
func (se *SignerServiceEndpoint) handleConnection(conn net.Conn) {
|
|
for {
|
|
if !se.IsRunning() {
|
|
return // Ignore error from listener closing.
|
|
}
|
|
|
|
// Reset the connection deadline
|
|
deadline := time.Now().Add(se.timeoutReadWrite)
|
|
err := conn.SetDeadline(deadline)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
req, err := readMsg(conn)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
se.Logger.Error("handleConnection readMsg", "err", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
res, err := handleRequest(req, se.chainID, se.privVal)
|
|
|
|
if err != nil {
|
|
// only log the error; we'll reply with an error in res
|
|
se.Logger.Error("handleConnection handleRequest", "err", err)
|
|
}
|
|
|
|
err = writeMsg(conn, res)
|
|
if err != nil {
|
|
se.Logger.Error("handleConnection writeMsg", "err", err)
|
|
return
|
|
}
|
|
}
|
|
}
|