github.com/evdatsion/aphelion-dpos-bft@v0.32.1/privval/signer_validator_endpoint.go (about) 1 package privval 2 3 import ( 4 "fmt" 5 "net" 6 "sync" 7 "time" 8 9 "github.com/evdatsion/aphelion-dpos-bft/crypto" 10 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 11 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 12 "github.com/evdatsion/aphelion-dpos-bft/types" 13 ) 14 15 const ( 16 defaultHeartbeatSeconds = 2 17 defaultMaxDialRetries = 10 18 ) 19 20 var ( 21 heartbeatPeriod = time.Second * defaultHeartbeatSeconds 22 ) 23 24 // SignerValidatorEndpointOption sets an optional parameter on the SocketVal. 25 type SignerValidatorEndpointOption func(*SignerValidatorEndpoint) 26 27 // SignerValidatorEndpointSetHeartbeat sets the period on which to check the liveness of the 28 // connected Signer connections. 29 func SignerValidatorEndpointSetHeartbeat(period time.Duration) SignerValidatorEndpointOption { 30 return func(sc *SignerValidatorEndpoint) { sc.heartbeatPeriod = period } 31 } 32 33 // SocketVal implements PrivValidator. 34 // It listens for an external process to dial in and uses 35 // the socket to request signatures. 36 type SignerValidatorEndpoint struct { 37 cmn.BaseService 38 39 listener net.Listener 40 41 // ping 42 cancelPingCh chan struct{} 43 pingTicker *time.Ticker 44 heartbeatPeriod time.Duration 45 46 // signer is mutable since it can be reset if the connection fails. 47 // failures are detected by a background ping routine. 48 // All messages are request/response, so we hold the mutex 49 // so only one request/response pair can happen at a time. 50 // Methods on the underlying net.Conn itself are already goroutine safe. 51 mtx sync.Mutex 52 53 // TODO: Signer should encapsulate and hide the endpoint completely. Invert the relation 54 signer *SignerRemote 55 } 56 57 // Check that SignerValidatorEndpoint implements PrivValidator. 58 var _ types.PrivValidator = (*SignerValidatorEndpoint)(nil) 59 60 // NewSignerValidatorEndpoint returns an instance of SignerValidatorEndpoint. 61 func NewSignerValidatorEndpoint(logger log.Logger, listener net.Listener) *SignerValidatorEndpoint { 62 sc := &SignerValidatorEndpoint{ 63 listener: listener, 64 heartbeatPeriod: heartbeatPeriod, 65 } 66 67 sc.BaseService = *cmn.NewBaseService(logger, "SignerValidatorEndpoint", sc) 68 69 return sc 70 } 71 72 //-------------------------------------------------------- 73 // Implement PrivValidator 74 75 // GetPubKey implements PrivValidator. 76 func (ve *SignerValidatorEndpoint) GetPubKey() crypto.PubKey { 77 ve.mtx.Lock() 78 defer ve.mtx.Unlock() 79 return ve.signer.GetPubKey() 80 } 81 82 // SignVote implements PrivValidator. 83 func (ve *SignerValidatorEndpoint) SignVote(chainID string, vote *types.Vote) error { 84 ve.mtx.Lock() 85 defer ve.mtx.Unlock() 86 return ve.signer.SignVote(chainID, vote) 87 } 88 89 // SignProposal implements PrivValidator. 90 func (ve *SignerValidatorEndpoint) SignProposal(chainID string, proposal *types.Proposal) error { 91 ve.mtx.Lock() 92 defer ve.mtx.Unlock() 93 return ve.signer.SignProposal(chainID, proposal) 94 } 95 96 //-------------------------------------------------------- 97 // More thread safe methods proxied to the signer 98 99 // Ping is used to check connection health. 100 func (ve *SignerValidatorEndpoint) Ping() error { 101 ve.mtx.Lock() 102 defer ve.mtx.Unlock() 103 return ve.signer.Ping() 104 } 105 106 // Close closes the underlying net.Conn. 107 func (ve *SignerValidatorEndpoint) Close() { 108 ve.mtx.Lock() 109 defer ve.mtx.Unlock() 110 if ve.signer != nil { 111 if err := ve.signer.Close(); err != nil { 112 ve.Logger.Error("OnStop", "err", err) 113 } 114 } 115 116 if ve.listener != nil { 117 if err := ve.listener.Close(); err != nil { 118 ve.Logger.Error("OnStop", "err", err) 119 } 120 } 121 } 122 123 //-------------------------------------------------------- 124 // Service start and stop 125 126 // OnStart implements cmn.Service. 127 func (ve *SignerValidatorEndpoint) OnStart() error { 128 if closed, err := ve.reset(); err != nil { 129 ve.Logger.Error("OnStart", "err", err) 130 return err 131 } else if closed { 132 return fmt.Errorf("listener is closed") 133 } 134 135 // Start a routine to keep the connection alive 136 ve.cancelPingCh = make(chan struct{}, 1) 137 ve.pingTicker = time.NewTicker(ve.heartbeatPeriod) 138 go func() { 139 for { 140 select { 141 case <-ve.pingTicker.C: 142 err := ve.Ping() 143 if err != nil { 144 ve.Logger.Error("Ping", "err", err) 145 if err == ErrUnexpectedResponse { 146 return 147 } 148 149 closed, err := ve.reset() 150 if err != nil { 151 ve.Logger.Error("Reconnecting to remote signer failed", "err", err) 152 continue 153 } 154 if closed { 155 ve.Logger.Info("listener is closing") 156 return 157 } 158 159 ve.Logger.Info("Re-created connection to remote signer", "impl", ve) 160 } 161 case <-ve.cancelPingCh: 162 ve.pingTicker.Stop() 163 return 164 } 165 } 166 }() 167 168 return nil 169 } 170 171 // OnStop implements cmn.Service. 172 func (ve *SignerValidatorEndpoint) OnStop() { 173 if ve.cancelPingCh != nil { 174 close(ve.cancelPingCh) 175 } 176 ve.Close() 177 } 178 179 //-------------------------------------------------------- 180 // Connection and signer management 181 182 // waits to accept and sets a new connection. 183 // connection is closed in OnStop. 184 // returns true if the listener is closed 185 // (ie. it returns a nil conn). 186 func (ve *SignerValidatorEndpoint) reset() (closed bool, err error) { 187 ve.mtx.Lock() 188 defer ve.mtx.Unlock() 189 190 // first check if the conn already exists and close it. 191 if ve.signer != nil { 192 if tmpErr := ve.signer.Close(); tmpErr != nil { 193 ve.Logger.Error("error closing socket val connection during reset", "err", tmpErr) 194 } 195 } 196 197 // wait for a new conn 198 conn, err := ve.acceptConnection() 199 if err != nil { 200 return false, err 201 } 202 203 // listener is closed 204 if conn == nil { 205 return true, nil 206 } 207 208 ve.signer, err = NewSignerRemote(conn) 209 if err != nil { 210 // failed to fetch the pubkey. close out the connection. 211 if tmpErr := conn.Close(); tmpErr != nil { 212 ve.Logger.Error("error closing connection", "err", tmpErr) 213 } 214 return false, err 215 } 216 return false, nil 217 } 218 219 // Attempt to accept a connection. 220 // Times out after the listener's timeoutAccept 221 func (ve *SignerValidatorEndpoint) acceptConnection() (net.Conn, error) { 222 conn, err := ve.listener.Accept() 223 if err != nil { 224 if !ve.IsRunning() { 225 return nil, nil // Ignore error from listener closing. 226 } 227 return nil, err 228 } 229 return conn, nil 230 }