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