github.com/klaytn/klaytn@v1.12.1/networks/p2p/peer.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2014 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from p2p/peer.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package p2p 22 23 import ( 24 "errors" 25 "fmt" 26 "io" 27 "net" 28 "sort" 29 "sync" 30 "sync/atomic" 31 "time" 32 33 "github.com/klaytn/klaytn/common" 34 "github.com/klaytn/klaytn/common/mclock" 35 "github.com/klaytn/klaytn/event" 36 "github.com/klaytn/klaytn/log" 37 "github.com/klaytn/klaytn/networks/p2p/discover" 38 "github.com/klaytn/klaytn/rlp" 39 ) 40 41 var logger = log.NewModuleLogger(log.NetworksP2P) 42 43 const ( 44 baseProtocolVersion = 5 45 baseProtocolLength = uint64(16) 46 baseProtocolMaxMsgSize = 2 * 1024 47 48 snappyProtocolVersion = 5 49 50 pingInterval = 15 * time.Second 51 ) 52 53 const ( 54 // devp2p message codes 55 handshakeMsg = 0x00 56 discMsg = 0x01 57 pingMsg = 0x02 58 pongMsg = 0x03 59 ) 60 61 const ( 62 ConnDefault = iota 63 ConnTxMsg 64 ) 65 66 // protoHandshake is the RLP structure of the protocol handshake. 67 type protoHandshake struct { 68 Version uint64 69 Name string 70 Caps []Cap 71 ListenPort []uint64 72 ID discover.NodeID 73 Multichannel bool 74 75 // Ignore additional fields (for forward compatibility). 76 Rest []rlp.RawValue `rlp:"tail"` 77 } 78 79 // PeerEventType is the type of peer events emitted by a p2p.Server 80 type PeerEventType string 81 82 const ( 83 // PeerEventTypeAdd is the type of event emitted when a peer is added 84 // to a p2p.Server 85 PeerEventTypeAdd PeerEventType = "add" 86 87 // PeerEventTypeDrop is the type of event emitted when a peer is 88 // dropped from a p2p.Server 89 PeerEventTypeDrop PeerEventType = "drop" 90 91 // PeerEventTypeMsgSend is the type of event emitted when a 92 // message is successfully sent to a peer 93 PeerEventTypeMsgSend PeerEventType = "msgsend" 94 95 // PeerEventTypeMsgRecv is the type of event emitted when a 96 // message is received from a peer 97 PeerEventTypeMsgRecv PeerEventType = "msgrecv" 98 ) 99 100 // PeerEvent is an event emitted when peers are either added or dropped from 101 // a p2p.Server or when a message is sent or received on a peer connection 102 type PeerEvent struct { 103 Type PeerEventType `json:"type"` 104 Peer discover.NodeID `json:"peer"` 105 Error string `json:"error,omitempty"` 106 Protocol string `json:"protocol,omitempty"` 107 MsgCode *uint64 `json:"msg_code,omitempty"` 108 MsgSize *uint32 `json:"msg_size,omitempty"` 109 } 110 111 // Peer represents a connected remote node. 112 type Peer struct { 113 rws []*conn 114 running map[string][]*protoRW 115 logger log.Logger 116 created mclock.AbsTime 117 118 wg sync.WaitGroup 119 protoErr chan error 120 closed chan struct{} 121 disc chan DiscReason 122 123 // events receives message send / receive events if set 124 events *event.Feed 125 } 126 127 // NewPeer returns a peer for testing purposes. 128 func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { 129 pipe, _ := net.Pipe() 130 conn := []*conn{{fd: pipe, transport: nil, id: id, caps: caps, name: name}} 131 peer, _ := newPeer(conn, nil, defaultRWTimerConfig) 132 close(peer.closed) // ensures Disconnect doesn't block 133 return peer 134 } 135 136 // ID returns the node's public key. 137 func (p *Peer) ID() discover.NodeID { 138 return p.rws[ConnDefault].id 139 } 140 141 // Name returns the node name that the remote node advertised. 142 func (p *Peer) Name() string { 143 return p.rws[ConnDefault].name 144 } 145 146 // RunningCap returns true if the peer is actively connected using any of the 147 // enumerated versions of a specific protocol, meaning that at least one of the 148 // versions is supported by both this node and the peer p. 149 func (p *Peer) RunningCap(protocol string, versions []uint) bool { 150 if protos, ok := p.running[protocol]; ok { 151 for _, ver := range versions { 152 if protos[ConnDefault].Version == ver { 153 return true 154 } 155 } 156 } 157 return false 158 } 159 160 // Caps returns the capabilities (supported subprotocols) of the remote peer. 161 func (p *Peer) Caps() []Cap { 162 // TODO: maybe return copy 163 return p.rws[ConnDefault].caps 164 } 165 166 // RemoteAddr returns the remote address of the network connection. 167 func (p *Peer) RemoteAddr() net.Addr { 168 return p.rws[ConnDefault].fd.RemoteAddr() 169 } 170 171 // LocalAddr returns the local address of the network connection. 172 func (p *Peer) LocalAddr() net.Addr { 173 return p.rws[ConnDefault].fd.LocalAddr() 174 } 175 176 // Disconnect terminates the peer connection with the given reason. 177 // It returns immediately and does not wait until the connection is closed. 178 func (p *Peer) Disconnect(reason DiscReason) { 179 select { 180 case p.disc <- reason: 181 case <-p.closed: 182 } 183 } 184 185 // String implements fmt.Stringer. 186 func (p *Peer) String() string { 187 return fmt.Sprintf("Peer %x %v", p.rws[ConnDefault].id[:8], p.RemoteAddr()) 188 } 189 190 // Inbound returns true if the peer is an inbound connection 191 func (p *Peer) Inbound() bool { 192 return p.rws[ConnDefault].flags&inboundConn != 0 193 } 194 195 // GetNumberInboundAndOutbound returns the number of 196 // inbound and outbound connections connected to the peer. 197 func (p *Peer) GetNumberInboundAndOutbound() (int, int) { 198 inbound, outbound := 0, 0 199 for _, rw := range p.rws { 200 if rw.flags&inboundConn != 0 { 201 inbound++ 202 } else { 203 outbound++ 204 } 205 } 206 return inbound, outbound 207 } 208 209 // newPeer should be called to create a peer. 210 func newPeer(conns []*conn, protocols []Protocol, tc RWTimerConfig) (*Peer, error) { 211 if conns == nil || len(conns) < 1 || conns[ConnDefault] == nil { 212 return nil, errors.New("conn is invalid") 213 } 214 msgReadWriters := make([]MsgReadWriter, len(conns)) 215 for i, c := range conns { 216 msgReadWriters[i] = c 217 } 218 protomap := matchProtocols(protocols, conns[ConnDefault].caps, msgReadWriters, tc) 219 p := &Peer{ 220 rws: conns, 221 running: protomap, 222 created: mclock.Now(), 223 disc: make(chan DiscReason), 224 protoErr: make(chan error, len(protomap)+len(conns)), // protocols + pingLoop 225 closed: make(chan struct{}), 226 logger: logger.NewWith("id", conns[ConnDefault].id, "conn", conns[ConnDefault].flags), 227 } 228 return p, nil 229 } 230 231 func (p *Peer) Log() log.Logger { 232 return p.logger 233 } 234 235 func (p *Peer) run() (remoteRequested bool, err error) { 236 var ( 237 writeStart = make(chan struct{}, 1) 238 writeErr = make(chan error, 1) 239 readErr = make(chan error, 1) 240 reason DiscReason // sent to the peer 241 ) 242 if len(p.rws) != 1 { 243 return false, errors.New("The size of rws should be 1") 244 } 245 p.wg.Add(2) 246 go p.readLoop(ConnDefault, p.rws[ConnDefault], readErr) 247 go p.pingLoop(p.rws[ConnDefault]) 248 249 // Start all protocol handlers. 250 writeStart <- struct{}{} 251 p.startProtocols(writeStart, writeErr) 252 253 // Wait for an error or disconnect. 254 loop: 255 for { 256 select { 257 case err = <-writeErr: 258 // A write finished. Allow the next write to start if 259 // there was no error. 260 if err != nil { 261 reason = DiscNetworkError 262 break loop 263 } 264 writeStart <- struct{}{} 265 case err = <-readErr: 266 if r, ok := err.(DiscReason); ok { 267 remoteRequested = true 268 reason = r 269 } else { 270 reason = DiscNetworkError 271 } 272 break loop 273 case err = <-p.protoErr: 274 reason = discReasonForError(err) 275 break loop 276 case err = <-p.disc: 277 reason = discReasonForError(err) 278 break loop 279 } 280 } 281 282 close(p.closed) 283 for _, rw := range p.rws { 284 rw.close(reason) 285 } 286 p.wg.Wait() 287 logger.Debug(fmt.Sprintf("run stopped, peer: %v", p.ID())) 288 return remoteRequested, err 289 } 290 291 // ErrorPeer is a peer error 292 type ErrorPeer struct { 293 remoteRequested bool 294 err error 295 } 296 297 // runWithRWs Runs peer 298 func (p *Peer) runWithRWs() (remoteRequested bool, err error) { 299 resultErr := make(chan ErrorPeer, len(p.rws)) 300 var errs ErrorPeer 301 302 var ( 303 writeStarts = make([]chan struct{}, 0, len(p.rws)) 304 writeErr = make([]chan error, 0, 1) 305 readErr = make([]chan error, 0, 1) 306 ) 307 308 for range p.rws { 309 writeStarts = append(writeStarts, make(chan struct{}, 1)) 310 writeErr = append(writeErr, make(chan error, 1)) 311 readErr = append(readErr, make(chan error, 1)) 312 } 313 314 for i, rw := range p.rws { 315 p.wg.Add(2) 316 go p.readLoop(i, rw, readErr[i]) 317 go p.pingLoop(rw) 318 writeStarts[i] <- struct{}{} 319 } 320 321 // Start all protocol handlers. 322 p.startProtocolsWithRWs(writeStarts, writeErr) 323 324 for i, rw := range p.rws { 325 p.wg.Add(1) 326 go p.handleError(rw, resultErr, writeErr[i], writeStarts[i], readErr[i]) 327 } 328 329 select { 330 case errs = <-resultErr: 331 close(p.closed) 332 } 333 p.wg.Wait() 334 logger.Debug(fmt.Sprintf("run stopped, peer: %v", p.ID())) 335 return errs.remoteRequested, errs.err 336 } 337 338 // handleError handles read, write, and protocol errors on rw 339 func (p *Peer) handleError(rw *conn, errCh chan<- ErrorPeer, writeErr <-chan error, writeStart chan<- struct{}, readErr <-chan error) { 340 defer p.wg.Done() 341 var errRW ErrorPeer 342 var reason DiscReason // sent to the peer 343 344 // Wait for an error or disconnect. 345 loop: 346 for { 347 select { 348 case errRW.err = <-writeErr: 349 // A write finished. Allow the next write to start if 350 // there was no error. 351 if errRW.err != nil { 352 reason = DiscNetworkError 353 break loop 354 } 355 writeStart <- struct{}{} 356 case errRW.err = <-readErr: 357 if r, ok := errRW.err.(DiscReason); ok { 358 errRW.remoteRequested = true 359 reason = r 360 } else { 361 reason = DiscNetworkError 362 } 363 break loop 364 case errRW.err = <-p.protoErr: 365 reason = discReasonForError(errRW.err) 366 break loop 367 case errRW.err = <-p.disc: 368 reason = discReasonForError(errRW.err) 369 break loop 370 case <-p.closed: 371 reason = DiscQuitting 372 break loop 373 } 374 } 375 376 rw.close(reason) 377 errCh <- errRW 378 } 379 380 func (p *Peer) pingLoop(rw *conn) { 381 ping := time.NewTimer(pingInterval) 382 defer p.wg.Done() 383 defer ping.Stop() 384 for { 385 select { 386 case <-ping.C: 387 if err := SendItems(rw, pingMsg); err != nil { 388 p.protoErr <- err 389 logger.Debug(fmt.Sprintf("pingLoop stopped, peer: %v", p.ID())) 390 return 391 } 392 ping.Reset(pingInterval) 393 case <-p.closed: 394 logger.Debug(fmt.Sprintf("pingLoop stopped, peer: %v", p.ID())) 395 return 396 } 397 } 398 } 399 400 func (p *Peer) readLoop(connectionOrder int, rw *conn, errc chan<- error) { 401 defer p.wg.Done() 402 for { 403 msg, err := rw.ReadMsg() 404 if err != nil { 405 errc <- err 406 logger.Debug(fmt.Sprintf("readLoop stopped, peer: %v", p.ID())) 407 return 408 } 409 msg.ReceivedAt = time.Now() 410 if err = p.handle(connectionOrder, rw, msg); err != nil { 411 errc <- err 412 logger.Debug(fmt.Sprintf("readLoop stopped, peer: %v", p.ID())) 413 return 414 } 415 } 416 } 417 418 func (p *Peer) handle(connectionOrder int, rw *conn, msg Msg) error { 419 switch { 420 case msg.Code == pingMsg: 421 msg.Discard() 422 go SendItems(rw, pongMsg) 423 case msg.Code == discMsg: 424 // This is the last message. We don't need to discard or 425 // check errors because, the connection will be closed after it. 426 var m struct{ R DiscReason } 427 rlp.Decode(msg.Payload, &m) 428 return m.R 429 case msg.Code < baseProtocolLength: 430 // ignore other base protocol messages 431 return msg.Discard() 432 default: 433 // it's a subprotocol message 434 proto, err := p.getProto(connectionOrder, msg.Code) 435 if err != nil { 436 return fmt.Errorf("msg code out of range: %v", msg.Code) 437 } 438 select { 439 case proto.in <- msg: 440 return nil 441 case <-p.closed: 442 return io.EOF 443 } 444 } 445 return nil 446 } 447 448 func countMatchingProtocols(protocols []Protocol, caps []Cap) int { 449 n := 0 450 for _, cap := range caps { 451 for _, proto := range protocols { 452 if proto.Name == cap.Name && proto.Version == cap.Version { 453 n++ 454 } 455 } 456 } 457 return n 458 } 459 460 // matchProtocols creates structures for matching named subprotocols. 461 func matchProtocols(protocols []Protocol, caps []Cap, rws []MsgReadWriter, tc RWTimerConfig) map[string][]*protoRW { 462 sort.Sort(capsByNameAndVersion(caps)) 463 offset := baseProtocolLength 464 result := make(map[string][]*protoRW) 465 466 outer: 467 for _, cap := range caps { 468 for _, proto := range protocols { 469 if proto.Name == cap.Name && proto.Version == cap.Version { 470 // If an old protocol version matched, revert it 471 if old := result[cap.Name]; old != nil { 472 offset -= old[ConnDefault].Length 473 } 474 // Assign the new match 475 protoRWs := make([]*protoRW, 0, len(rws)) 476 if rws == nil || len(rws) == 0 { 477 protoRWs = []*protoRW{{Protocol: proto, offset: offset, in: make(chan Msg), w: nil, tc: tc}} 478 } else { 479 for _, rw := range rws { 480 protoRWs = append(protoRWs, &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw, tc: tc}) 481 } 482 } 483 result[cap.Name] = protoRWs 484 offset += proto.Length 485 486 continue outer 487 } 488 } 489 } 490 return result 491 } 492 493 func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { 494 p.wg.Add(len(p.running)) 495 for _, protos := range p.running { 496 if len(protos) != 1 { 497 p.logger.Error("The size of protos should be 1", "size", len(protos)) 498 p.protoErr <- errProtocolReturned 499 return 500 } 501 proto := protos[ConnDefault] 502 proto.closed = p.closed 503 proto.wstart = writeStart 504 proto.werr = writeErr 505 proto.tc = defaultRWTimerConfig 506 var rw MsgReadWriter = proto 507 if p.events != nil { 508 rw = newMsgEventer(rw, p.events, p.ID(), proto.Name) 509 } 510 p.logger.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version)) 511 go func() { 512 // p.wg.Add(1) 513 defer p.wg.Done() 514 err := proto.Run(p, rw) 515 if err == nil { 516 p.logger.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version)) 517 err = errProtocolReturned 518 } else if err != io.EOF { 519 p.logger.Error(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err) 520 } 521 p.protoErr <- err 522 p.logger.Debug(fmt.Sprintf("Protocol go routine stopped, peer: %v", p.ID())) 523 // p.wg.Done() 524 }() 525 } 526 } 527 528 // startProtocolsWithRWs run the protocol using several RWs. 529 func (p *Peer) startProtocolsWithRWs(writeStarts []chan struct{}, writeErrs []chan error) { 530 p.wg.Add(len(p.running)) 531 532 for _, protos := range p.running { 533 rws := make([]MsgReadWriter, 0, len(protos)) 534 protos := protos 535 for i, proto := range protos { 536 proto.closed = p.closed 537 if len(writeStarts) > i { 538 proto.wstart = writeStarts[i] 539 } else { 540 writeErrs[i] <- errors.New("WriteStartsChannelSize") 541 } 542 proto.werr = writeErrs[i] 543 544 var rw MsgReadWriter = proto 545 if p.events != nil { 546 rw = newMsgEventer(rw, p.events, p.ID(), proto.Name) 547 } 548 rws = append(rws, rw) 549 } 550 551 p.logger.Trace(fmt.Sprintf("Starting protocol %s/%d", protos[ConnDefault].Name, protos[ConnDefault].Version)) 552 go func() { 553 // p.wg.Add(1) 554 defer p.wg.Done() 555 err := protos[ConnDefault].RunWithRWs(p, rws) 556 if err == nil { 557 p.logger.Trace(fmt.Sprintf("Protocol %s/%d returned", protos[ConnDefault].Name, protos[ConnDefault].Version)) 558 err = errProtocolReturned 559 } else if err != io.EOF { 560 p.logger.Error(fmt.Sprintf("Protocol %s/%d failed", protos[ConnDefault].Name, protos[ConnDefault].Version), "err", err) 561 } 562 p.protoErr <- err 563 p.logger.Debug(fmt.Sprintf("Protocol go routine stopped, peer: %v", p.ID())) 564 // p.wg.Done() 565 }() 566 } 567 } 568 569 // getProto finds the protocol responsible for handling 570 // the given message code. 571 func (p *Peer) getProto(connectionOrder int, code uint64) (*protoRW, error) { 572 for _, proto := range p.running { 573 if code >= proto[connectionOrder].offset && code < proto[connectionOrder].offset+proto[connectionOrder].Length { 574 return proto[connectionOrder], nil 575 } 576 } 577 return nil, newPeerError(errInvalidMsgCode, "%d", code) 578 } 579 580 type RWTimerConfig struct { 581 Interval uint64 582 WaitTime time.Duration 583 } 584 585 var defaultRWTimerConfig = RWTimerConfig{1000, 15 * time.Second} 586 587 type protoRW struct { 588 Protocol 589 in chan Msg // receices read messages 590 closed <-chan struct{} // receives when peer is shutting down 591 wstart <-chan struct{} // receives when write may start 592 werr chan<- error // for write results 593 offset uint64 594 w MsgWriter 595 count uint64 // count the number of WriteMsg calls 596 tc RWTimerConfig 597 } 598 599 func (rw *protoRW) WriteMsg(msg Msg) (err error) { 600 if msg.Code >= rw.Length { 601 return newPeerError(errInvalidMsgCode, "not handled, (code %x) (size %d)", msg.Code, msg.Size) 602 } 603 msg.Code += rw.offset 604 rwCount := atomic.AddUint64(&rw.count, 1) 605 if rwCount%rw.tc.Interval == 0 { 606 timer := time.NewTimer(rw.tc.WaitTime) 607 defer timer.Stop() 608 select { 609 case <-rw.wstart: 610 err = rw.w.WriteMsg(msg) 611 // Report write status back to Peer.run. It will initiate 612 // shutdown if the error is non-nil and unblock the next write 613 // otherwise. The calling protocol code should exit for errors 614 // as well but we don't want to rely on that. 615 case <-rw.closed: 616 err = fmt.Errorf("shutting down") 617 return err 618 case <-timer.C: 619 writeMsgTimeOutCounter.Inc(1) 620 err = fmt.Errorf("failed to write message for %v", rw.tc.WaitTime) 621 } 622 } else { 623 select { 624 case <-rw.wstart: 625 err = rw.w.WriteMsg(msg) 626 case <-rw.closed: 627 err = fmt.Errorf("shutting down") 628 return err 629 } 630 } 631 select { 632 case rw.werr <- err: 633 default: 634 } 635 return err 636 } 637 638 func (rw *protoRW) ReadMsg() (Msg, error) { 639 select { 640 case msg := <-rw.in: 641 msg.Code -= rw.offset 642 return msg, nil 643 case <-rw.closed: 644 return Msg{}, io.EOF 645 } 646 } 647 648 // NetworkInfo represents the connection information with the peer. 649 type NetworkInfo struct { 650 LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection 651 RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection 652 Inbound bool `json:"inbound"` 653 Trusted bool `json:"trusted"` 654 Static bool `json:"static"` 655 NodeType string `json:"nodeType"` 656 } 657 658 // PeerInfo represents a short summary of the information known about a connected 659 // peer. Sub-protocol independent fields are contained and initialized here, with 660 // protocol specifics delegated to all connected sub-protocols. 661 type PeerInfo struct { 662 ID string `json:"id"` // Unique node identifier (also the encryption key) 663 Name string `json:"name"` // Name of the node, including client type, version, OS, custom data 664 Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer 665 Networks []NetworkInfo `json:"networks"` // Networks is all the NetworkInfo associated with the peer 666 Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields 667 } 668 669 // Info gathers and returns a collection of metadata known about a peer. 670 func (p *Peer) Info() *PeerInfo { 671 // Gather the protocol capabilities 672 var caps []string 673 for _, cap := range p.Caps() { 674 caps = append(caps, cap.String()) 675 } 676 // Assemble the generic peer metadata 677 info := &PeerInfo{ 678 ID: p.ID().String(), 679 Name: p.Name(), 680 Caps: caps, 681 Protocols: make(map[string]interface{}), 682 } 683 684 for _, rw := range p.rws { 685 var network NetworkInfo 686 network.LocalAddress = rw.fd.LocalAddr().String() 687 network.RemoteAddress = rw.fd.RemoteAddr().String() 688 network.Inbound = rw.is(inboundConn) 689 network.Trusted = rw.is(trustedConn) 690 network.Static = rw.is(staticDialedConn) 691 switch rw.conntype { 692 case common.CONSENSUSNODE: 693 network.NodeType = "cn" 694 case common.ENDPOINTNODE: 695 network.NodeType = "en" 696 case common.PROXYNODE: 697 network.NodeType = "pn" 698 case common.BOOTNODE: 699 network.NodeType = "bn" 700 default: 701 network.NodeType = "unknown" 702 } 703 info.Networks = append(info.Networks, network) 704 } 705 706 // Gather all the running protocol infos 707 for _, proto := range p.running { 708 protoInfo := interface{}("unknown") 709 if query := proto[ConnDefault].Protocol.PeerInfo; query != nil { 710 if metadata := query(p.ID()); metadata != nil { 711 protoInfo = metadata 712 } else { 713 protoInfo = "handshake" 714 } 715 } 716 info.Protocols[proto[ConnDefault].Name] = protoInfo 717 } 718 return info 719 } 720 721 func (p *Peer) ConnType() common.ConnType { 722 return p.rws[ConnDefault].conntype 723 } 724 725 type PeerTypeValidator interface { 726 // ValidatePeerType returns nil if successful. Otherwise, it returns an error object. 727 ValidatePeerType(addr common.Address) error 728 }