github.com/evdatsion/aphelion-dpos-bft@v0.32.1/privval/signer_service_endpoint.go (about) 1 package privval 2 3 import ( 4 "io" 5 "net" 6 "time" 7 8 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 9 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 10 "github.com/evdatsion/aphelion-dpos-bft/types" 11 ) 12 13 // SignerServiceEndpointOption sets an optional parameter on the SignerServiceEndpoint. 14 type SignerServiceEndpointOption func(*SignerServiceEndpoint) 15 16 // SignerServiceEndpointTimeoutReadWrite sets the read and write timeout for connections 17 // from external signing processes. 18 func SignerServiceEndpointTimeoutReadWrite(timeout time.Duration) SignerServiceEndpointOption { 19 return func(ss *SignerServiceEndpoint) { ss.timeoutReadWrite = timeout } 20 } 21 22 // SignerServiceEndpointConnRetries sets the amount of attempted retries to connect. 23 func SignerServiceEndpointConnRetries(retries int) SignerServiceEndpointOption { 24 return func(ss *SignerServiceEndpoint) { ss.connRetries = retries } 25 } 26 27 // SignerServiceEndpoint dials using its dialer and responds to any 28 // signature requests using its privVal. 29 type SignerServiceEndpoint struct { 30 cmn.BaseService 31 32 chainID string 33 timeoutReadWrite time.Duration 34 connRetries int 35 privVal types.PrivValidator 36 37 dialer SocketDialer 38 conn net.Conn 39 } 40 41 // NewSignerServiceEndpoint returns a SignerServiceEndpoint that will dial using the given 42 // dialer and respond to any signature requests over the connection 43 // using the given privVal. 44 func NewSignerServiceEndpoint( 45 logger log.Logger, 46 chainID string, 47 privVal types.PrivValidator, 48 dialer SocketDialer, 49 ) *SignerServiceEndpoint { 50 se := &SignerServiceEndpoint{ 51 chainID: chainID, 52 timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds, 53 connRetries: defaultMaxDialRetries, 54 privVal: privVal, 55 dialer: dialer, 56 } 57 58 se.BaseService = *cmn.NewBaseService(logger, "SignerServiceEndpoint", se) 59 return se 60 } 61 62 // OnStart implements cmn.Service. 63 func (se *SignerServiceEndpoint) OnStart() error { 64 conn, err := se.connect() 65 if err != nil { 66 se.Logger.Error("OnStart", "err", err) 67 return err 68 } 69 70 se.conn = conn 71 go se.handleConnection(conn) 72 73 return nil 74 } 75 76 // OnStop implements cmn.Service. 77 func (se *SignerServiceEndpoint) OnStop() { 78 if se.conn == nil { 79 return 80 } 81 82 if err := se.conn.Close(); err != nil { 83 se.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed")) 84 } 85 } 86 87 func (se *SignerServiceEndpoint) connect() (net.Conn, error) { 88 for retries := 0; retries < se.connRetries; retries++ { 89 // Don't sleep if it is the first retry. 90 if retries > 0 { 91 time.Sleep(se.timeoutReadWrite) 92 } 93 94 conn, err := se.dialer() 95 if err == nil { 96 return conn, nil 97 } 98 99 se.Logger.Error("dialing", "err", err) 100 } 101 102 return nil, ErrDialRetryMax 103 } 104 105 func (se *SignerServiceEndpoint) handleConnection(conn net.Conn) { 106 for { 107 if !se.IsRunning() { 108 return // Ignore error from listener closing. 109 } 110 111 // Reset the connection deadline 112 deadline := time.Now().Add(se.timeoutReadWrite) 113 err := conn.SetDeadline(deadline) 114 if err != nil { 115 return 116 } 117 118 req, err := readMsg(conn) 119 if err != nil { 120 if err != io.EOF { 121 se.Logger.Error("handleConnection readMsg", "err", err) 122 } 123 return 124 } 125 126 res, err := handleRequest(req, se.chainID, se.privVal) 127 128 if err != nil { 129 // only log the error; we'll reply with an error in res 130 se.Logger.Error("handleConnection handleRequest", "err", err) 131 } 132 133 err = writeMsg(conn, res) 134 if err != nil { 135 se.Logger.Error("handleConnection writeMsg", "err", err) 136 return 137 } 138 } 139 }