github.com/ethereum/go-ethereum@v1.16.1/p2p/peer.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package p2p 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "net" 24 "slices" 25 "sync" 26 "time" 27 28 "github.com/ethereum/go-ethereum/common/mclock" 29 "github.com/ethereum/go-ethereum/event" 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/metrics" 32 "github.com/ethereum/go-ethereum/p2p/enode" 33 "github.com/ethereum/go-ethereum/p2p/enr" 34 "github.com/ethereum/go-ethereum/rlp" 35 ) 36 37 var ( 38 ErrShuttingDown = errors.New("shutting down") 39 ) 40 41 const ( 42 baseProtocolVersion = 5 43 baseProtocolLength = uint64(16) 44 baseProtocolMaxMsgSize = 2 * 1024 45 46 snappyProtocolVersion = 5 47 48 pingInterval = 15 * time.Second 49 ) 50 51 const ( 52 // devp2p message codes 53 handshakeMsg = 0x00 54 discMsg = 0x01 55 pingMsg = 0x02 56 pongMsg = 0x03 57 ) 58 59 // protoHandshake is the RLP structure of the protocol handshake. 60 type protoHandshake struct { 61 Version uint64 62 Name string 63 Caps []Cap 64 ListenPort uint64 65 ID []byte // secp256k1 public key 66 67 // Ignore additional fields (for forward compatibility). 68 Rest []rlp.RawValue `rlp:"tail"` 69 } 70 71 // PeerEventType is the type of peer events emitted by a p2p.Server 72 type PeerEventType string 73 74 const ( 75 // PeerEventTypeAdd is the type of event emitted when a peer is added 76 // to a p2p.Server 77 PeerEventTypeAdd PeerEventType = "add" 78 79 // PeerEventTypeDrop is the type of event emitted when a peer is 80 // dropped from a p2p.Server 81 PeerEventTypeDrop PeerEventType = "drop" 82 83 // PeerEventTypeMsgSend is the type of event emitted when a 84 // message is successfully sent to a peer 85 PeerEventTypeMsgSend PeerEventType = "msgsend" 86 87 // PeerEventTypeMsgRecv is the type of event emitted when a 88 // message is received from a peer 89 PeerEventTypeMsgRecv PeerEventType = "msgrecv" 90 ) 91 92 // PeerEvent is an event emitted when peers are either added or dropped from 93 // a p2p.Server or when a message is sent or received on a peer connection 94 type PeerEvent struct { 95 Type PeerEventType `json:"type"` 96 Peer enode.ID `json:"peer"` 97 Error string `json:"error,omitempty"` 98 Protocol string `json:"protocol,omitempty"` 99 MsgCode *uint64 `json:"msg_code,omitempty"` 100 MsgSize *uint32 `json:"msg_size,omitempty"` 101 LocalAddress string `json:"local,omitempty"` 102 RemoteAddress string `json:"remote,omitempty"` 103 } 104 105 // Peer represents a connected remote node. 106 type Peer struct { 107 rw *conn 108 running map[string]*protoRW 109 log log.Logger 110 created mclock.AbsTime 111 112 wg sync.WaitGroup 113 protoErr chan error 114 closed chan struct{} 115 pingRecv chan struct{} 116 disc chan DiscReason 117 118 // events receives message send / receive events if set 119 events *event.Feed 120 testPipe *MsgPipeRW // for testing 121 } 122 123 // NewPeer returns a peer for testing purposes. 124 func NewPeer(id enode.ID, name string, caps []Cap) *Peer { 125 // Generate a fake set of local protocols to match as running caps. Almost 126 // no fields needs to be meaningful here as we're only using it to cross- 127 // check with the "remote" caps array. 128 protos := make([]Protocol, len(caps)) 129 for i, cap := range caps { 130 protos[i].Name = cap.Name 131 protos[i].Version = cap.Version 132 } 133 pipe, _ := net.Pipe() 134 node := enode.SignNull(new(enr.Record), id) 135 conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name} 136 peer := newPeer(log.Root(), conn, protos) 137 close(peer.closed) // ensures Disconnect doesn't block 138 return peer 139 } 140 141 // NewPeerPipe creates a peer for testing purposes. 142 // The message pipe given as the last parameter is closed when 143 // Disconnect is called on the peer. 144 func NewPeerPipe(id enode.ID, name string, caps []Cap, pipe *MsgPipeRW) *Peer { 145 p := NewPeer(id, name, caps) 146 p.testPipe = pipe 147 return p 148 } 149 150 // ID returns the node's public key. 151 func (p *Peer) ID() enode.ID { 152 return p.rw.node.ID() 153 } 154 155 // Node returns the peer's node descriptor. 156 func (p *Peer) Node() *enode.Node { 157 return p.rw.node 158 } 159 160 // Name returns an abbreviated form of the name 161 func (p *Peer) Name() string { 162 s := p.rw.name 163 if len(s) > 20 { 164 return s[:20] + "..." 165 } 166 return s 167 } 168 169 // Fullname returns the node name that the remote node advertised. 170 func (p *Peer) Fullname() string { 171 return p.rw.name 172 } 173 174 // Caps returns the capabilities (supported subprotocols) of the remote peer. 175 func (p *Peer) Caps() []Cap { 176 // TODO: maybe return copy 177 return p.rw.caps 178 } 179 180 // RunningCap returns true if the peer is actively connected using any of the 181 // enumerated versions of a specific protocol, meaning that at least one of the 182 // versions is supported by both this node and the peer p. 183 func (p *Peer) RunningCap(protocol string, versions []uint) bool { 184 if proto, ok := p.running[protocol]; ok { 185 for _, ver := range versions { 186 if proto.Version == ver { 187 return true 188 } 189 } 190 } 191 return false 192 } 193 194 // RemoteAddr returns the remote address of the network connection. 195 func (p *Peer) RemoteAddr() net.Addr { 196 return p.rw.fd.RemoteAddr() 197 } 198 199 // LocalAddr returns the local address of the network connection. 200 func (p *Peer) LocalAddr() net.Addr { 201 return p.rw.fd.LocalAddr() 202 } 203 204 // Disconnect terminates the peer connection with the given reason. 205 // It returns immediately and does not wait until the connection is closed. 206 func (p *Peer) Disconnect(reason DiscReason) { 207 if p.testPipe != nil { 208 p.testPipe.Close() 209 } 210 211 select { 212 case p.disc <- reason: 213 case <-p.closed: 214 } 215 } 216 217 // String implements fmt.Stringer. 218 func (p *Peer) String() string { 219 id := p.ID() 220 return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr()) 221 } 222 223 // Inbound returns true if the peer is an inbound (not dialed) connection. 224 func (p *Peer) Inbound() bool { 225 return p.rw.is(inboundConn) 226 } 227 228 // Trusted returns true if the peer is configured as trusted. 229 // Trusted peers are accepted in above the MaxInboundConns limit. 230 // The peer can be either inbound or dialed. 231 func (p *Peer) Trusted() bool { 232 return p.rw.is(trustedConn) 233 } 234 235 // DynDialed returns true if the peer was dialed successfully (passed handshake) and 236 // it is not configured as static. 237 func (p *Peer) DynDialed() bool { 238 return p.rw.is(dynDialedConn) 239 } 240 241 // StaticDialed returns true if the peer was dialed successfully (passed handshake) and 242 // it is configured as static. 243 func (p *Peer) StaticDialed() bool { 244 return p.rw.is(staticDialedConn) 245 } 246 247 // Lifetime returns the time since peer creation. 248 func (p *Peer) Lifetime() mclock.AbsTime { 249 return mclock.Now() - p.created 250 } 251 252 func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { 253 protomap := matchProtocols(protocols, conn.caps, conn) 254 p := &Peer{ 255 rw: conn, 256 running: protomap, 257 created: mclock.Now(), 258 disc: make(chan DiscReason), 259 protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop 260 closed: make(chan struct{}), 261 pingRecv: make(chan struct{}, 16), 262 log: log.New("id", conn.node.ID(), "conn", conn.flags), 263 } 264 return p 265 } 266 267 func (p *Peer) Log() log.Logger { 268 return p.log 269 } 270 271 func (p *Peer) run() (remoteRequested bool, err error) { 272 var ( 273 writeStart = make(chan struct{}, 1) 274 writeErr = make(chan error, 1) 275 readErr = make(chan error, 1) 276 reason DiscReason // sent to the peer 277 ) 278 p.wg.Add(2) 279 go p.readLoop(readErr) 280 go p.pingLoop() 281 live1min := time.NewTimer(1 * time.Minute) 282 defer live1min.Stop() 283 284 // Start all protocol handlers. 285 writeStart <- struct{}{} 286 p.startProtocols(writeStart, writeErr) 287 288 // Wait for an error or disconnect. 289 loop: 290 for { 291 select { 292 case err = <-writeErr: 293 // A write finished. Allow the next write to start if 294 // there was no error. 295 if err != nil { 296 reason = DiscNetworkError 297 break loop 298 } 299 writeStart <- struct{}{} 300 case err = <-readErr: 301 if r, ok := err.(DiscReason); ok { 302 remoteRequested = true 303 reason = r 304 } else { 305 reason = DiscNetworkError 306 } 307 break loop 308 case err = <-p.protoErr: 309 reason = discReasonForError(err) 310 break loop 311 case err = <-p.disc: 312 reason = discReasonForError(err) 313 break loop 314 case <-live1min.C: 315 if p.Inbound() { 316 serve1MinSuccessMeter.Mark(1) 317 } else { 318 dial1MinSuccessMeter.Mark(1) 319 } 320 } 321 } 322 323 close(p.closed) 324 p.rw.close(reason) 325 p.wg.Wait() 326 return remoteRequested, err 327 } 328 329 func (p *Peer) pingLoop() { 330 defer p.wg.Done() 331 332 ping := time.NewTimer(pingInterval) 333 defer ping.Stop() 334 335 for { 336 select { 337 case <-ping.C: 338 if err := SendItems(p.rw, pingMsg); err != nil { 339 p.protoErr <- err 340 return 341 } 342 ping.Reset(pingInterval) 343 344 case <-p.pingRecv: 345 SendItems(p.rw, pongMsg) 346 347 case <-p.closed: 348 return 349 } 350 } 351 } 352 353 func (p *Peer) readLoop(errc chan<- error) { 354 defer p.wg.Done() 355 for { 356 msg, err := p.rw.ReadMsg() 357 if err != nil { 358 errc <- err 359 return 360 } 361 msg.ReceivedAt = time.Now() 362 if err = p.handle(msg); err != nil { 363 errc <- err 364 return 365 } 366 } 367 } 368 369 func (p *Peer) handle(msg Msg) error { 370 switch { 371 case msg.Code == pingMsg: 372 msg.Discard() 373 select { 374 case p.pingRecv <- struct{}{}: 375 case <-p.closed: 376 } 377 case msg.Code == discMsg: 378 // This is the last message. We don't need to discard or 379 // check errors because, the connection will be closed after it. 380 return decodeDisconnectMessage(msg.Payload) 381 case msg.Code < baseProtocolLength: 382 // ignore other base protocol messages 383 return msg.Discard() 384 default: 385 // it's a subprotocol message 386 proto, err := p.getProto(msg.Code) 387 if err != nil { 388 return fmt.Errorf("msg code out of range: %v", msg.Code) 389 } 390 if metrics.Enabled() { 391 m := fmt.Sprintf("%s/%s/%d/%#02x", ingressMeterName, proto.Name, proto.Version, msg.Code-proto.offset) 392 metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) 393 metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) 394 } 395 select { 396 case proto.in <- msg: 397 return nil 398 case <-p.closed: 399 return io.EOF 400 } 401 } 402 return nil 403 } 404 405 // decodeDisconnectMessage decodes the payload of discMsg. 406 func decodeDisconnectMessage(r io.Reader) (reason DiscReason) { 407 s := rlp.NewStream(r, 100) 408 k, _, err := s.Kind() 409 if err != nil { 410 return DiscInvalid 411 } 412 if k == rlp.List { 413 s.List() 414 err = s.Decode(&reason) 415 } else { 416 // Legacy path: some implementations, including geth, used to send the disconnect 417 // reason as a byte array by accident. 418 err = s.Decode(&reason) 419 } 420 if err != nil { 421 reason = DiscInvalid 422 } 423 return reason 424 } 425 426 func countMatchingProtocols(protocols []Protocol, caps []Cap) int { 427 n := 0 428 for _, cap := range caps { 429 for _, proto := range protocols { 430 if proto.Name == cap.Name && proto.Version == cap.Version { 431 n++ 432 } 433 } 434 } 435 return n 436 } 437 438 // matchProtocols creates structures for matching named subprotocols. 439 func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { 440 slices.SortFunc(caps, Cap.Cmp) 441 offset := baseProtocolLength 442 result := make(map[string]*protoRW) 443 444 outer: 445 for _, cap := range caps { 446 for _, proto := range protocols { 447 if proto.Name == cap.Name && proto.Version == cap.Version { 448 // If an old protocol version matched, revert it 449 if old := result[cap.Name]; old != nil { 450 offset -= old.Length 451 } 452 // Assign the new match 453 result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} 454 offset += proto.Length 455 456 continue outer 457 } 458 } 459 } 460 return result 461 } 462 463 func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { 464 p.wg.Add(len(p.running)) 465 for _, proto := range p.running { 466 proto.closed = p.closed 467 proto.wstart = writeStart 468 proto.werr = writeErr 469 var rw MsgReadWriter = proto 470 if p.events != nil { 471 rw = newMsgEventer(rw, p.events, p.ID(), proto.Name, p.Info().Network.RemoteAddress, p.Info().Network.LocalAddress) 472 } 473 p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version)) 474 go func() { 475 defer p.wg.Done() 476 err := proto.Run(p, rw) 477 if err == nil { 478 p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version)) 479 err = errProtocolReturned 480 } else if !errors.Is(err, io.EOF) { 481 p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err) 482 } 483 p.protoErr <- err 484 }() 485 } 486 } 487 488 // getProto finds the protocol responsible for handling 489 // the given message code. 490 func (p *Peer) getProto(code uint64) (*protoRW, error) { 491 for _, proto := range p.running { 492 if code >= proto.offset && code < proto.offset+proto.Length { 493 return proto, nil 494 } 495 } 496 return nil, newPeerError(errInvalidMsgCode, "%d", code) 497 } 498 499 type protoRW struct { 500 Protocol 501 in chan Msg // receives read messages 502 closed <-chan struct{} // receives when peer is shutting down 503 wstart <-chan struct{} // receives when write may start 504 werr chan<- error // for write results 505 offset uint64 506 w MsgWriter 507 } 508 509 func (rw *protoRW) WriteMsg(msg Msg) (err error) { 510 if msg.Code >= rw.Length { 511 return newPeerError(errInvalidMsgCode, "not handled") 512 } 513 msg.meterCap = rw.cap() 514 msg.meterCode = msg.Code 515 516 msg.Code += rw.offset 517 518 select { 519 case <-rw.wstart: 520 err = rw.w.WriteMsg(msg) 521 // Report write status back to Peer.run. It will initiate 522 // shutdown if the error is non-nil and unblock the next write 523 // otherwise. The calling protocol code should exit for errors 524 // as well but we don't want to rely on that. 525 rw.werr <- err 526 case <-rw.closed: 527 err = ErrShuttingDown 528 } 529 return err 530 } 531 532 func (rw *protoRW) ReadMsg() (Msg, error) { 533 select { 534 case msg := <-rw.in: 535 msg.Code -= rw.offset 536 return msg, nil 537 case <-rw.closed: 538 return Msg{}, io.EOF 539 } 540 } 541 542 // PeerInfo represents a short summary of the information known about a connected 543 // peer. Sub-protocol independent fields are contained and initialized here, with 544 // protocol specifics delegated to all connected sub-protocols. 545 type PeerInfo struct { 546 ENR string `json:"enr,omitempty"` // Ethereum Node Record 547 Enode string `json:"enode"` // Node URL 548 ID string `json:"id"` // Unique node identifier 549 Name string `json:"name"` // Name of the node, including client type, version, OS, custom data 550 Caps []string `json:"caps"` // Protocols advertised by this peer 551 Network struct { 552 LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection 553 RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection 554 Inbound bool `json:"inbound"` 555 Trusted bool `json:"trusted"` 556 Static bool `json:"static"` 557 } `json:"network"` 558 Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields 559 } 560 561 // Info gathers and returns a collection of metadata known about a peer. 562 func (p *Peer) Info() *PeerInfo { 563 // Gather the protocol capabilities 564 var caps []string 565 for _, cap := range p.Caps() { 566 caps = append(caps, cap.String()) 567 } 568 // Assemble the generic peer metadata 569 info := &PeerInfo{ 570 Enode: p.Node().URLv4(), 571 ID: p.ID().String(), 572 Name: p.Fullname(), 573 Caps: caps, 574 Protocols: make(map[string]interface{}, len(p.running)), 575 } 576 if p.Node().Seq() > 0 { 577 info.ENR = p.Node().String() 578 } 579 info.Network.LocalAddress = p.LocalAddr().String() 580 info.Network.RemoteAddress = p.RemoteAddr().String() 581 info.Network.Inbound = p.rw.is(inboundConn) 582 info.Network.Trusted = p.rw.is(trustedConn) 583 info.Network.Static = p.rw.is(staticDialedConn) 584 585 // Gather all the running protocol infos 586 for _, proto := range p.running { 587 protoInfo := interface{}("unknown") 588 if query := proto.Protocol.PeerInfo; query != nil { 589 if metadata := query(p.ID()); metadata != nil { 590 protoInfo = metadata 591 } else { 592 protoInfo = "handshake" 593 } 594 } 595 info.Protocols[proto.Name] = protoInfo 596 } 597 return info 598 }