github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/peer.go (about) 1 package p2p 2 3 import ( 4 "fmt" 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 11 tmconn "github.com/evdatsion/aphelion-dpos-bft/p2p/conn" 12 ) 13 14 const metricsTickerDuration = 10 * time.Second 15 16 // Peer is an interface representing a peer connected on a reactor. 17 type Peer interface { 18 cmn.Service 19 FlushStop() 20 21 ID() ID // peer's cryptographic ID 22 RemoteIP() net.IP // remote IP of the connection 23 RemoteAddr() net.Addr // remote address of the connection 24 25 IsOutbound() bool // did we dial the peer 26 IsPersistent() bool // do we redial this peer when we disconnect 27 28 CloseConn() error // close original connection 29 30 NodeInfo() NodeInfo // peer's info 31 Status() tmconn.ConnectionStatus 32 SocketAddr() *NetAddress // actual address of the socket 33 34 Send(byte, []byte) bool 35 TrySend(byte, []byte) bool 36 37 Set(string, interface{}) 38 Get(string) interface{} 39 } 40 41 //---------------------------------------------------------- 42 43 // peerConn contains the raw connection and its config. 44 type peerConn struct { 45 outbound bool 46 persistent bool 47 conn net.Conn // source connection 48 49 socketAddr *NetAddress 50 51 // cached RemoteIP() 52 ip net.IP 53 } 54 55 func newPeerConn( 56 outbound, persistent bool, 57 conn net.Conn, 58 socketAddr *NetAddress, 59 ) peerConn { 60 61 return peerConn{ 62 outbound: outbound, 63 persistent: persistent, 64 conn: conn, 65 socketAddr: socketAddr, 66 } 67 } 68 69 // ID only exists for SecretConnection. 70 // NOTE: Will panic if conn is not *SecretConnection. 71 func (pc peerConn) ID() ID { 72 return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey()) 73 } 74 75 // Return the IP from the connection RemoteAddr 76 func (pc peerConn) RemoteIP() net.IP { 77 if pc.ip != nil { 78 return pc.ip 79 } 80 81 host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String()) 82 if err != nil { 83 panic(err) 84 } 85 86 ips, err := net.LookupIP(host) 87 if err != nil { 88 panic(err) 89 } 90 91 pc.ip = ips[0] 92 93 return pc.ip 94 } 95 96 // peer implements Peer. 97 // 98 // Before using a peer, you will need to perform a handshake on connection. 99 type peer struct { 100 cmn.BaseService 101 102 // raw peerConn and the multiplex connection 103 peerConn 104 mconn *tmconn.MConnection 105 106 // peer's node info and the channel it knows about 107 // channels = nodeInfo.Channels 108 // cached to avoid copying nodeInfo in hasChannel 109 nodeInfo NodeInfo 110 channels []byte 111 112 // User data 113 Data *cmn.CMap 114 115 metrics *Metrics 116 metricsTicker *time.Ticker 117 } 118 119 type PeerOption func(*peer) 120 121 func newPeer( 122 pc peerConn, 123 mConfig tmconn.MConnConfig, 124 nodeInfo NodeInfo, 125 reactorsByCh map[byte]Reactor, 126 chDescs []*tmconn.ChannelDescriptor, 127 onPeerError func(Peer, interface{}), 128 options ...PeerOption, 129 ) *peer { 130 p := &peer{ 131 peerConn: pc, 132 nodeInfo: nodeInfo, 133 channels: nodeInfo.(DefaultNodeInfo).Channels, // TODO 134 Data: cmn.NewCMap(), 135 metricsTicker: time.NewTicker(metricsTickerDuration), 136 metrics: NopMetrics(), 137 } 138 139 p.mconn = createMConnection( 140 pc.conn, 141 p, 142 reactorsByCh, 143 chDescs, 144 onPeerError, 145 mConfig, 146 ) 147 p.BaseService = *cmn.NewBaseService(nil, "Peer", p) 148 for _, option := range options { 149 option(p) 150 } 151 152 return p 153 } 154 155 // String representation. 156 func (p *peer) String() string { 157 if p.outbound { 158 return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID()) 159 } 160 161 return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID()) 162 } 163 164 //--------------------------------------------------- 165 // Implements cmn.Service 166 167 // SetLogger implements BaseService. 168 func (p *peer) SetLogger(l log.Logger) { 169 p.Logger = l 170 p.mconn.SetLogger(l) 171 } 172 173 // OnStart implements BaseService. 174 func (p *peer) OnStart() error { 175 if err := p.BaseService.OnStart(); err != nil { 176 return err 177 } 178 179 if err := p.mconn.Start(); err != nil { 180 return err 181 } 182 183 go p.metricsReporter() 184 return nil 185 } 186 187 // FlushStop mimics OnStop but additionally ensures that all successful 188 // .Send() calls will get flushed before closing the connection. 189 // NOTE: it is not safe to call this method more than once. 190 func (p *peer) FlushStop() { 191 p.metricsTicker.Stop() 192 p.BaseService.OnStop() 193 p.mconn.FlushStop() // stop everything and close the conn 194 } 195 196 // OnStop implements BaseService. 197 func (p *peer) OnStop() { 198 p.metricsTicker.Stop() 199 p.BaseService.OnStop() 200 p.mconn.Stop() // stop everything and close the conn 201 } 202 203 //--------------------------------------------------- 204 // Implements Peer 205 206 // ID returns the peer's ID - the hex encoded hash of its pubkey. 207 func (p *peer) ID() ID { 208 return p.nodeInfo.ID() 209 } 210 211 // IsOutbound returns true if the connection is outbound, false otherwise. 212 func (p *peer) IsOutbound() bool { 213 return p.peerConn.outbound 214 } 215 216 // IsPersistent returns true if the peer is persitent, false otherwise. 217 func (p *peer) IsPersistent() bool { 218 return p.peerConn.persistent 219 } 220 221 // NodeInfo returns a copy of the peer's NodeInfo. 222 func (p *peer) NodeInfo() NodeInfo { 223 return p.nodeInfo 224 } 225 226 // SocketAddr returns the address of the socket. 227 // For outbound peers, it's the address dialed (after DNS resolution). 228 // For inbound peers, it's the address returned by the underlying connection 229 // (not what's reported in the peer's NodeInfo). 230 func (p *peer) SocketAddr() *NetAddress { 231 return p.peerConn.socketAddr 232 } 233 234 // Status returns the peer's ConnectionStatus. 235 func (p *peer) Status() tmconn.ConnectionStatus { 236 return p.mconn.Status() 237 } 238 239 // Send msg bytes to the channel identified by chID byte. Returns false if the 240 // send queue is full after timeout, specified by MConnection. 241 func (p *peer) Send(chID byte, msgBytes []byte) bool { 242 if !p.IsRunning() { 243 // see Switch#Broadcast, where we fetch the list of peers and loop over 244 // them - while we're looping, one peer may be removed and stopped. 245 return false 246 } else if !p.hasChannel(chID) { 247 return false 248 } 249 res := p.mconn.Send(chID, msgBytes) 250 if res { 251 labels := []string{ 252 "peer_id", string(p.ID()), 253 "chID", fmt.Sprintf("%#x", chID), 254 } 255 p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) 256 } 257 return res 258 } 259 260 // TrySend msg bytes to the channel identified by chID byte. Immediately returns 261 // false if the send queue is full. 262 func (p *peer) TrySend(chID byte, msgBytes []byte) bool { 263 if !p.IsRunning() { 264 return false 265 } else if !p.hasChannel(chID) { 266 return false 267 } 268 res := p.mconn.TrySend(chID, msgBytes) 269 if res { 270 labels := []string{ 271 "peer_id", string(p.ID()), 272 "chID", fmt.Sprintf("%#x", chID), 273 } 274 p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) 275 } 276 return res 277 } 278 279 // Get the data for a given key. 280 func (p *peer) Get(key string) interface{} { 281 return p.Data.Get(key) 282 } 283 284 // Set sets the data for the given key. 285 func (p *peer) Set(key string, data interface{}) { 286 p.Data.Set(key, data) 287 } 288 289 // hasChannel returns true if the peer reported 290 // knowing about the given chID. 291 func (p *peer) hasChannel(chID byte) bool { 292 for _, ch := range p.channels { 293 if ch == chID { 294 return true 295 } 296 } 297 // NOTE: probably will want to remove this 298 // but could be helpful while the feature is new 299 p.Logger.Debug( 300 "Unknown channel for peer", 301 "channel", 302 chID, 303 "channels", 304 p.channels, 305 ) 306 return false 307 } 308 309 // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all. 310 func (p *peer) CloseConn() error { 311 return p.peerConn.conn.Close() 312 } 313 314 //--------------------------------------------------- 315 // methods only used for testing 316 // TODO: can we remove these? 317 318 // CloseConn closes the underlying connection 319 func (pc *peerConn) CloseConn() { 320 pc.conn.Close() // nolint: errcheck 321 } 322 323 // RemoteAddr returns peer's remote network address. 324 func (p *peer) RemoteAddr() net.Addr { 325 return p.peerConn.conn.RemoteAddr() 326 } 327 328 // CanSend returns true if the send queue is not full, false otherwise. 329 func (p *peer) CanSend(chID byte) bool { 330 if !p.IsRunning() { 331 return false 332 } 333 return p.mconn.CanSend(chID) 334 } 335 336 //--------------------------------------------------- 337 338 func PeerMetrics(metrics *Metrics) PeerOption { 339 return func(p *peer) { 340 p.metrics = metrics 341 } 342 } 343 344 func (p *peer) metricsReporter() { 345 for { 346 select { 347 case <-p.metricsTicker.C: 348 status := p.mconn.Status() 349 var sendQueueSize float64 350 for _, chStatus := range status.Channels { 351 sendQueueSize += float64(chStatus.SendQueueSize) 352 } 353 354 p.metrics.PeerPendingSendBytes.With("peer_id", string(p.ID())).Set(sendQueueSize) 355 case <-p.Quit(): 356 return 357 } 358 } 359 } 360 361 //------------------------------------------------------------------ 362 // helper funcs 363 364 func createMConnection( 365 conn net.Conn, 366 p *peer, 367 reactorsByCh map[byte]Reactor, 368 chDescs []*tmconn.ChannelDescriptor, 369 onPeerError func(Peer, interface{}), 370 config tmconn.MConnConfig, 371 ) *tmconn.MConnection { 372 373 onReceive := func(chID byte, msgBytes []byte) { 374 reactor := reactorsByCh[chID] 375 if reactor == nil { 376 // Note that its ok to panic here as it's caught in the conn._recover, 377 // which does onPeerError. 378 panic(fmt.Sprintf("Unknown channel %X", chID)) 379 } 380 labels := []string{ 381 "peer_id", string(p.ID()), 382 "chID", fmt.Sprintf("%#x", chID), 383 } 384 p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes))) 385 reactor.Receive(chID, p, msgBytes) 386 } 387 388 onError := func(r interface{}) { 389 onPeerError(p, r) 390 } 391 392 return tmconn.NewMConnectionWithConfig( 393 conn, 394 chDescs, 395 onReceive, 396 onError, 397 config, 398 ) 399 }