package privval
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"time"
|
|
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
// IPCRemoteSignerOption sets an optional parameter on the IPCRemoteSigner.
|
|
type IPCRemoteSignerOption func(*IPCRemoteSigner)
|
|
|
|
// IPCRemoteSignerConnDeadline sets the read and write deadline for connections
|
|
// from external signing processes.
|
|
func IPCRemoteSignerConnDeadline(deadline time.Duration) IPCRemoteSignerOption {
|
|
return func(ss *IPCRemoteSigner) { ss.connDeadline = deadline }
|
|
}
|
|
|
|
// IPCRemoteSignerConnRetries sets the amount of attempted retries to connect.
|
|
func IPCRemoteSignerConnRetries(retries int) IPCRemoteSignerOption {
|
|
return func(ss *IPCRemoteSigner) { ss.connRetries = retries }
|
|
}
|
|
|
|
// IPCRemoteSigner is a RPC implementation of PrivValidator that listens on a unix socket.
|
|
type IPCRemoteSigner struct {
|
|
cmn.BaseService
|
|
|
|
addr string
|
|
chainID string
|
|
connDeadline time.Duration
|
|
connRetries int
|
|
privVal types.PrivValidator
|
|
|
|
listener *net.UnixListener
|
|
}
|
|
|
|
// NewIPCRemoteSigner returns an instance of IPCRemoteSigner.
|
|
func NewIPCRemoteSigner(
|
|
logger log.Logger,
|
|
chainID, socketAddr string,
|
|
privVal types.PrivValidator,
|
|
) *IPCRemoteSigner {
|
|
rs := &IPCRemoteSigner{
|
|
addr: socketAddr,
|
|
chainID: chainID,
|
|
connDeadline: time.Second * defaultConnDeadlineSeconds,
|
|
connRetries: defaultDialRetries,
|
|
privVal: privVal,
|
|
}
|
|
|
|
rs.BaseService = *cmn.NewBaseService(logger, "IPCRemoteSigner", rs)
|
|
|
|
return rs
|
|
}
|
|
|
|
// OnStart implements cmn.Service.
|
|
func (rs *IPCRemoteSigner) OnStart() error {
|
|
err := rs.listen()
|
|
if err != nil {
|
|
err = cmn.ErrorWrap(err, "listen")
|
|
rs.Logger.Error("OnStart", "err", err)
|
|
return err
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
conn, err := rs.listener.AcceptUnix()
|
|
if err != nil {
|
|
return
|
|
}
|
|
go rs.handleConnection(conn)
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnStop implements cmn.Service.
|
|
func (rs *IPCRemoteSigner) OnStop() {
|
|
if rs.listener != nil {
|
|
if err := rs.listener.Close(); err != nil {
|
|
rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (rs *IPCRemoteSigner) listen() error {
|
|
la, err := net.ResolveUnixAddr("unix", rs.addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rs.listener, err = net.ListenUnix("unix", la)
|
|
|
|
return err
|
|
}
|
|
|
|
func (rs *IPCRemoteSigner) handleConnection(conn net.Conn) {
|
|
for {
|
|
if !rs.IsRunning() {
|
|
return // Ignore error from listener closing.
|
|
}
|
|
|
|
// Reset the connection deadline
|
|
conn.SetDeadline(time.Now().Add(rs.connDeadline))
|
|
|
|
req, err := readMsg(conn)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
rs.Logger.Error("handleConnection", "err", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
res, err := handleRequest(req, rs.chainID, rs.privVal)
|
|
|
|
if err != nil {
|
|
// only log the error; we'll reply with an error in res
|
|
rs.Logger.Error("handleConnection", "err", err)
|
|
}
|
|
|
|
err = writeMsg(conn, res)
|
|
if err != nil {
|
|
rs.Logger.Error("handleConnection", "err", err)
|
|
return
|
|
}
|
|
}
|
|
}
|