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