package privval import ( "fmt" "io" "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" ) // RemoteSignerOption sets an optional parameter on the RemoteSigner. type RemoteSignerOption func(*RemoteSigner) // RemoteSignerConnDeadline sets the read and write deadline for connections // from external signing processes. func RemoteSignerConnDeadline(deadline time.Duration) RemoteSignerOption { return func(ss *RemoteSigner) { ss.connDeadline = deadline } } // RemoteSignerConnRetries sets the amount of attempted retries to connect. func RemoteSignerConnRetries(retries int) RemoteSignerOption { return func(ss *RemoteSigner) { ss.connRetries = retries } } // RemoteSigner implements PrivValidator by dialing to a socket. type RemoteSigner struct { cmn.BaseService addr string chainID string connDeadline time.Duration connRetries int privKey ed25519.PrivKeyEd25519 privVal types.PrivValidator conn net.Conn } // NewRemoteSigner returns an instance of RemoteSigner. func NewRemoteSigner( logger log.Logger, chainID, socketAddr string, privVal types.PrivValidator, privKey ed25519.PrivKeyEd25519, ) *RemoteSigner { rs := &RemoteSigner{ addr: socketAddr, chainID: chainID, connDeadline: time.Second * defaultConnDeadlineSeconds, connRetries: defaultDialRetries, privKey: privKey, privVal: privVal, } rs.BaseService = *cmn.NewBaseService(logger, "RemoteSigner", rs) return rs } // OnStart implements cmn.Service. func (rs *RemoteSigner) OnStart() error { go rs.handleConnection() return nil } // OnStop implements cmn.Service. func (rs *RemoteSigner) OnStop() { if rs.conn == nil { return } if err := rs.conn.Close(); err != nil { rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed")) } } func (rs *RemoteSigner) connect() (net.Conn, error) { //for retries := rs.connRetries; retries > 0; retries-- { // Don't sleep if it is the first retry. //if retries != rs.connRetries { // time.Sleep(rs.connDeadline) //} conn, err := cmn.Connect(rs.addr) if err != nil { rs.Logger.Error( "connect", "addr", rs.addr, "err", err, ) return nil, err } //if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil { // rs.Logger.Error( // "connect", // "err", err, // ) // //continue // return nil, err //} conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey) if err != nil { rs.Logger.Error( "connect", "err", err, ) return nil, err } fmt.Println("connected", conn.RemoteAddr()) return conn, nil //} return nil, ErrDialRetryMax } func (rs *RemoteSigner) handleConnection() { for { // establish connection loop: if !rs.IsRunning() { return // Ignore error from listener closing. } fmt.Println("Connecting again ...") conn, err := rs.connect() if err != nil { rs.Logger.Error("OnStart", "err", err) fmt.Println("failed to connect", err) time.Sleep(rs.connDeadline) continue } // Reset the connection deadline //conn.SetDeadline(time.Now().Add(rs.connDeadline*10)) for { // handle request loop req, err := readMsg(conn) if err != nil { if err != io.EOF { rs.Logger.Error("handleConnection readMsg", "err", err) } conn.Close() break } 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 handleRequest", "err", err) } err = writeMsg(conn, res) if err != nil { rs.Logger.Error("handleConnection writeMsg", "err", err) break } } } }