github.com/Finschia/ostracon@v1.1.5/p2p/peer.go (about) 1 package p2p 2 3 import ( 4 "fmt" 5 "net" 6 "reflect" 7 "time" 8 9 "github.com/gogo/protobuf/proto" 10 11 "github.com/Finschia/ostracon/libs/cmap" 12 "github.com/Finschia/ostracon/libs/log" 13 "github.com/Finschia/ostracon/libs/service" 14 15 tmconn "github.com/Finschia/ostracon/p2p/conn" 16 ) 17 18 //go:generate ../scripts/mockery_generate.sh Peer 19 //go:generate ../scripts/mockery_generate.sh EnvelopeSender 20 21 const metricsTickerDuration = 10 * time.Second 22 23 // Peer is an interface representing a peer connected on a reactor. 24 type Peer interface { 25 service.Service 26 FlushStop() 27 28 ID() ID // peer's cryptographic ID 29 RemoteIP() net.IP // remote IP of the connection 30 RemoteAddr() net.Addr // remote address of the connection 31 32 IsOutbound() bool // did we dial the peer 33 IsPersistent() bool // do we redial this peer when we disconnect 34 35 CloseConn() error // close original connection 36 37 NodeInfo() NodeInfo // peer's info 38 Status() tmconn.ConnectionStatus 39 SocketAddr() *NetAddress // actual address of the socket 40 41 // Deprecated: entities looking to act as peers should implement SendEnvelope instead. 42 // Send will be removed in v0.37. 43 Send(byte, []byte) bool 44 45 // Deprecated: entities looking to act as peers should implement TrySendEnvelope instead. 46 // TrySend will be removed in v0.37. 47 TrySend(byte, []byte) bool 48 49 Set(string, interface{}) 50 Get(string) interface{} 51 52 SetRemovalFailed() 53 GetRemovalFailed() bool 54 } 55 56 type BufferedMsg struct { 57 ChID byte 58 Peer Peer 59 Msg []byte 60 ProtoMsg proto.Message 61 } 62 63 type EnvelopeSender interface { 64 SendEnvelope(Envelope) bool 65 TrySendEnvelope(Envelope) bool 66 } 67 68 // EnvelopeSendShim implements a shim to allow the legacy peer type that 69 // does not implement SendEnvelope to be used in places where envelopes are 70 // being sent. If the peer implements the *Envelope methods, then they are used, 71 // otherwise, the message is marshaled and dispatched to the legacy *Send. 72 // 73 // Deprecated: Will be removed in v0.37. 74 func SendEnvelopeShim(p Peer, e Envelope, lg log.Logger) bool { 75 if es, ok := p.(EnvelopeSender); ok { 76 return es.SendEnvelope(e) 77 } 78 msg := e.Message 79 if w, ok := msg.(Wrapper); ok { 80 msg = w.Wrap() 81 } 82 msgBytes, err := proto.Marshal(msg) 83 if err != nil { 84 lg.Error("marshaling message to send", "error", err) 85 return false 86 } 87 return p.Send(e.ChannelID, msgBytes) 88 } 89 90 // EnvelopeTrySendShim implements a shim to allow the legacy peer type that 91 // does not implement TrySendEnvelope to be used in places where envelopes are 92 // being sent. If the peer implements the *Envelope methods, then they are used, 93 // otherwise, the message is marshaled and dispatched to the legacy *Send. 94 // 95 // Deprecated: Will be removed in v0.37. 96 func TrySendEnvelopeShim(p Peer, e Envelope, lg log.Logger) bool { 97 if es, ok := p.(EnvelopeSender); ok { 98 return es.SendEnvelope(e) 99 } 100 msg := e.Message 101 if w, ok := msg.(Wrapper); ok { 102 msg = w.Wrap() 103 } 104 msgBytes, err := proto.Marshal(msg) 105 if err != nil { 106 lg.Error("marshaling message to send", "error", err) 107 return false 108 } 109 return p.TrySend(e.ChannelID, msgBytes) 110 } 111 112 //---------------------------------------------------------- 113 114 // peerConn contains the raw connection and its config. 115 type peerConn struct { 116 outbound bool 117 persistent bool 118 conn net.Conn // source connection 119 120 socketAddr *NetAddress 121 122 // cached RemoteIP() 123 ip net.IP 124 } 125 126 func newPeerConn( 127 outbound, persistent bool, 128 conn net.Conn, 129 socketAddr *NetAddress, 130 ) peerConn { 131 132 return peerConn{ 133 outbound: outbound, 134 persistent: persistent, 135 conn: conn, 136 socketAddr: socketAddr, 137 } 138 } 139 140 // ID only exists for SecretConnection. 141 // NOTE: Will panic if conn is not *SecretConnection. 142 func (pc peerConn) ID() ID { 143 return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey()) 144 } 145 146 // Return the IP from the connection RemoteAddr 147 func (pc peerConn) RemoteIP() net.IP { 148 if pc.ip != nil { 149 return pc.ip 150 } 151 152 host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String()) 153 if err != nil { 154 panic(err) 155 } 156 157 ips, err := net.LookupIP(host) 158 if err != nil { 159 panic(err) 160 } 161 162 pc.ip = ips[0] 163 164 return pc.ip 165 } 166 167 // peer implements Peer. 168 // 169 // Before using a peer, you will need to perform a handshake on connection. 170 type peer struct { 171 service.BaseService 172 173 // raw peerConn and the multiplex connection 174 peerConn 175 mconn *tmconn.MConnection 176 177 // peer's node info and the channel it knows about 178 // channels = nodeInfo.Channels 179 // cached to avoid copying nodeInfo in hasChannel 180 nodeInfo NodeInfo 181 channels []byte 182 183 // User data 184 Data *cmap.CMap 185 186 metrics *Metrics 187 metricsTicker *time.Ticker 188 mlc *metricsLabelCache 189 190 // When removal of a peer fails, we set this flag 191 removalAttemptFailed bool 192 } 193 194 type PeerOption func(*peer) 195 196 func newPeer( 197 pc peerConn, 198 mConfig tmconn.MConnConfig, 199 nodeInfo NodeInfo, 200 reactorsByCh map[byte]Reactor, 201 msgTypeByChID map[byte]proto.Message, 202 chDescs []*tmconn.ChannelDescriptor, 203 onPeerError func(Peer, interface{}), 204 mlc *metricsLabelCache, 205 options ...PeerOption, 206 ) *peer { 207 p := &peer{ 208 peerConn: pc, 209 nodeInfo: nodeInfo, 210 channels: nodeInfo.(DefaultNodeInfo).Channels, 211 Data: cmap.NewCMap(), 212 metricsTicker: time.NewTicker(metricsTickerDuration), 213 metrics: NopMetrics(), 214 mlc: mlc, 215 } 216 217 p.mconn = createMConnection( 218 pc.conn, 219 p, 220 reactorsByCh, 221 msgTypeByChID, 222 chDescs, 223 onPeerError, 224 mConfig, 225 ) 226 p.BaseService = *service.NewBaseService(nil, "Peer", p) 227 for _, option := range options { 228 option(p) 229 } 230 231 return p 232 } 233 234 // String representation. 235 func (p *peer) String() string { 236 if p.outbound { 237 return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID()) 238 } 239 240 return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID()) 241 } 242 243 //--------------------------------------------------- 244 // Implements service.Service 245 246 // SetLogger implements BaseService. 247 func (p *peer) SetLogger(l log.Logger) { 248 p.Logger = l 249 p.mconn.SetLogger(l) 250 } 251 252 // OnStart implements BaseService. 253 func (p *peer) OnStart() error { 254 if err := p.BaseService.OnStart(); err != nil { 255 return err 256 } 257 258 if err := p.mconn.Start(); err != nil { 259 return err 260 } 261 262 go p.metricsReporter() 263 return nil 264 } 265 266 // FlushStop mimics OnStop but additionally ensures that all successful 267 // SendEnvelope() calls will get flushed before closing the connection. 268 // NOTE: it is not safe to call this method more than once. 269 func (p *peer) FlushStop() { 270 p.metricsTicker.Stop() 271 p.BaseService.OnStop() 272 p.mconn.FlushStop() // stop everything and close the conn 273 } 274 275 // OnStop implements BaseService. 276 func (p *peer) OnStop() { 277 p.metricsTicker.Stop() 278 p.BaseService.OnStop() 279 if err := p.mconn.Stop(); err != nil { // stop everything and close the conn 280 p.Logger.Debug("Error while stopping peer", "err", err) 281 } 282 } 283 284 //--------------------------------------------------- 285 // Implements Peer 286 287 // ID returns the peer's ID - the hex encoded hash of its pubkey. 288 func (p *peer) ID() ID { 289 return p.nodeInfo.ID() 290 } 291 292 // IsOutbound returns true if the connection is outbound, false otherwise. 293 func (p *peer) IsOutbound() bool { 294 return p.peerConn.outbound 295 } 296 297 // IsPersistent returns true if the peer is persitent, false otherwise. 298 func (p *peer) IsPersistent() bool { 299 return p.peerConn.persistent 300 } 301 302 // NodeInfo returns a copy of the peer's NodeInfo. 303 func (p *peer) NodeInfo() NodeInfo { 304 return p.nodeInfo 305 } 306 307 // SocketAddr returns the address of the socket. 308 // For outbound peers, it's the address dialed (after DNS resolution). 309 // For inbound peers, it's the address returned by the underlying connection 310 // (not what's reported in the peer's NodeInfo). 311 func (p *peer) SocketAddr() *NetAddress { 312 return p.peerConn.socketAddr 313 } 314 315 // Status returns the peer's ConnectionStatus. 316 func (p *peer) Status() tmconn.ConnectionStatus { 317 return p.mconn.Status() 318 } 319 320 // SendEnvelope sends the message in the envelope on the channel specified by the 321 // envelope. Returns false if the connection times out trying to place the message 322 // onto its internal queue. 323 // Using SendEnvelope allows for tracking the message bytes sent and received by message type 324 // as a metric which Send cannot support. 325 func (p *peer) SendEnvelope(e Envelope) bool { 326 if !p.IsRunning() { 327 return false 328 } else if !p.hasChannel(e.ChannelID) { 329 return false 330 } 331 msg := e.Message 332 metricLabelValue := p.mlc.ValueToMetricLabel(msg) 333 if w, ok := msg.(Wrapper); ok { 334 msg = w.Wrap() 335 } 336 msgBytes, err := proto.Marshal(msg) 337 if err != nil { 338 p.Logger.Error("marshaling message to send", "error", err) 339 return false 340 } 341 res := p.Send(e.ChannelID, msgBytes) 342 if res { 343 p.metrics.MessageSendBytesTotal.With("message_type", metricLabelValue).Add(float64(len(msgBytes))) 344 } 345 return res 346 } 347 348 // Send msg bytes to the channel identified by chID byte. Returns false if the 349 // send queue is full after timeout, specified by MConnection. 350 // SendEnvelope replaces Send which will be deprecated in a future release. 351 func (p *peer) Send(chID byte, msgBytes []byte) bool { 352 if !p.IsRunning() { 353 return false 354 } else if !p.hasChannel(chID) { 355 return false 356 } 357 res := p.mconn.Send(chID, msgBytes) 358 if res { 359 labels := []string{ 360 "peer_id", string(p.ID()), 361 "chID", fmt.Sprintf("%#x", chID), 362 } 363 p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) 364 } 365 return res 366 } 367 368 // TrySendEnvelope attempts to sends the message in the envelope on the channel specified by the 369 // envelope. Returns false immediately if the connection's internal queue is full 370 // Using TrySendEnvelope allows for tracking the message bytes sent and received by message type 371 // as a metric which TrySend cannot support. 372 func (p *peer) TrySendEnvelope(e Envelope) bool { 373 if !p.IsRunning() { 374 // see Switch#Broadcast, where we fetch the list of peers and loop over 375 // them - while we're looping, one peer may be removed and stopped. 376 return false 377 } else if !p.hasChannel(e.ChannelID) { 378 return false 379 } 380 msg := e.Message 381 metricLabelValue := p.mlc.ValueToMetricLabel(msg) 382 if w, ok := msg.(Wrapper); ok { 383 msg = w.Wrap() 384 } 385 msgBytes, err := proto.Marshal(msg) 386 if err != nil { 387 p.Logger.Error("marshaling message to send", "error", err) 388 return false 389 } 390 res := p.TrySend(e.ChannelID, msgBytes) 391 if res { 392 p.metrics.MessageSendBytesTotal.With("message_type", metricLabelValue).Add(float64(len(msgBytes))) 393 } 394 return res 395 } 396 397 // TrySend msg bytes to the channel identified by chID byte. Immediately returns 398 // false if the send queue is full. 399 // TrySendEnvelope replaces TrySend which will be deprecated in a future release. 400 func (p *peer) TrySend(chID byte, msgBytes []byte) bool { 401 if !p.IsRunning() { 402 return false 403 } else if !p.hasChannel(chID) { 404 return false 405 } 406 res := p.mconn.TrySend(chID, msgBytes) 407 if res { 408 labels := []string{ 409 "peer_id", string(p.ID()), 410 "chID", fmt.Sprintf("%#x", chID), 411 } 412 p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) 413 } 414 return res 415 } 416 417 // Get the data for a given key. 418 func (p *peer) Get(key string) interface{} { 419 return p.Data.Get(key) 420 } 421 422 // Set sets the data for the given key. 423 func (p *peer) Set(key string, data interface{}) { 424 p.Data.Set(key, data) 425 } 426 427 // hasChannel returns true if the peer reported 428 // knowing about the given chID. 429 func (p *peer) hasChannel(chID byte) bool { 430 for _, ch := range p.channels { 431 if ch == chID { 432 return true 433 } 434 } 435 // NOTE: probably will want to remove this 436 // but could be helpful while the feature is new 437 p.Logger.Debug( 438 "Unknown channel for peer", 439 "channel", 440 chID, 441 "channels", 442 p.channels, 443 ) 444 return false 445 } 446 447 // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all. 448 func (p *peer) CloseConn() error { 449 return p.peerConn.conn.Close() 450 } 451 452 func (p *peer) SetRemovalFailed() { 453 p.removalAttemptFailed = true 454 } 455 456 func (p *peer) GetRemovalFailed() bool { 457 return p.removalAttemptFailed 458 } 459 460 //--------------------------------------------------- 461 // methods only used for testing 462 // TODO: can we remove these? 463 464 // CloseConn closes the underlying connection 465 func (pc *peerConn) CloseConn() { 466 pc.conn.Close() 467 } 468 469 // RemoteAddr returns peer's remote network address. 470 func (p *peer) RemoteAddr() net.Addr { 471 return p.peerConn.conn.RemoteAddr() 472 } 473 474 // CanSend returns true if the send queue is not full, false otherwise. 475 func (p *peer) CanSend(chID byte) bool { 476 if !p.IsRunning() { 477 return false 478 } 479 return p.mconn.CanSend(chID) 480 } 481 482 //--------------------------------------------------- 483 484 func PeerMetrics(metrics *Metrics) PeerOption { 485 return func(p *peer) { 486 p.metrics = metrics 487 } 488 } 489 490 func (p *peer) metricsReporter() { 491 for { 492 select { 493 case <-p.metricsTicker.C: 494 status := p.mconn.Status() 495 var sendQueueSize float64 496 for _, chStatus := range status.Channels { 497 sendQueueSize += float64(chStatus.SendQueueSize) 498 } 499 500 p.metrics.PeerPendingSendBytes.With("peer_id", string(p.ID())).Set(sendQueueSize) 501 case <-p.Quit(): 502 return 503 } 504 } 505 } 506 507 //------------------------------------------------------------------ 508 // helper funcs 509 510 func createMConnection( 511 conn net.Conn, 512 p *peer, 513 reactorsByCh map[byte]Reactor, 514 msgTypeByChID map[byte]proto.Message, 515 chDescs []*tmconn.ChannelDescriptor, 516 onPeerError func(Peer, interface{}), 517 config tmconn.MConnConfig, 518 ) *tmconn.MConnection { 519 520 onReceive := func(chID byte, msgBytes []byte) { 521 reactor := reactorsByCh[chID] 522 if reactor == nil { 523 // Note that its ok to panic here as it's caught in the conn._recover, 524 // which does onPeerError. 525 panic(fmt.Sprintf("Unknown channel %X", chID)) 526 } 527 mt := msgTypeByChID[chID] 528 msg := proto.Clone(mt) 529 err := proto.Unmarshal(msgBytes, msg) 530 if err != nil { 531 panic(fmt.Errorf("unmarshaling message: %s into type: %s", err, reflect.TypeOf(mt))) 532 } 533 labels := []string{ 534 "peer_id", string(p.ID()), 535 "chID", fmt.Sprintf("%#x", chID), 536 } 537 if w, ok := msg.(Unwrapper); ok { 538 msg, err = w.Unwrap() 539 if err != nil { 540 panic(fmt.Errorf("unwrapping message: %s", err)) 541 } 542 } 543 p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes))) 544 p.metrics.MessageReceiveBytesTotal.With("message_type", p.mlc.ValueToMetricLabel(msg)).Add(float64(len(msgBytes))) 545 if config.RecvAsync { 546 ch := reactor.GetRecvChan() 547 p.metrics.NumPooledPeerMsgs.With(labels...).Set(float64(len(ch))) 548 // we must use copied msgBytes 549 // because msgBytes is on socket receive buffer yet so reactor can read it concurrently 550 copied := make([]byte, len(msgBytes)) 551 copy(copied, msgBytes) 552 // if the channel is full, we are blocking a message until it can send into the channel 553 ch <- &BufferedMsg{ChID: chID, Peer: p, Msg: copied, ProtoMsg: msg} 554 } else if nr, ok := reactor.(EnvelopeReceiver); ok { 555 nr.ReceiveEnvelope(Envelope{ 556 ChannelID: chID, 557 Src: p, 558 Message: msg, 559 }) 560 } else { 561 reactor.Receive(chID, p, msgBytes) 562 } 563 } 564 565 onError := func(r interface{}) { 566 onPeerError(p, r) 567 } 568 569 return tmconn.NewMConnectionWithConfig( 570 conn, 571 chDescs, 572 onReceive, 573 onError, 574 config, 575 ) 576 }