github.com/core-coin/go-core/v2@v2.1.9/p2p/peer.go (about) 1 // Copyright 2014 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core 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 "sort" 25 "sync" 26 "time" 27 28 "github.com/core-coin/go-core/v2/common/mclock" 29 "github.com/core-coin/go-core/v2/event" 30 "github.com/core-coin/go-core/v2/log" 31 "github.com/core-coin/go-core/v2/metrics" 32 "github.com/core-coin/go-core/v2/p2p/enode" 33 "github.com/core-coin/go-core/v2/p2p/enr" 34 "github.com/core-coin/go-core/v2/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 // ed448 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 disc chan DiscReason 116 117 // events receives message send / receive events if set 118 events *event.Feed 119 } 120 121 // NewPeer returns a peer for testing purposes. 122 func NewPeer(id enode.ID, name string, caps []Cap) *Peer { 123 pipe, _ := net.Pipe() 124 node := enode.SignNull(new(enr.Record), id) 125 conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name} 126 peer := newPeer(log.Root(), conn, nil) 127 close(peer.closed) // ensures Disconnect doesn't block 128 return peer 129 } 130 131 // ID returns the node's public key. 132 func (p *Peer) ID() enode.ID { 133 return p.rw.node.ID() 134 } 135 136 // Node returns the peer's node descriptor. 137 func (p *Peer) Node() *enode.Node { 138 return p.rw.node 139 } 140 141 // Name returns an abbreviated form of the name 142 func (p *Peer) Name() string { 143 s := p.rw.name 144 if len(s) > 50 { 145 return s[:50] + "..." 146 } 147 return s 148 } 149 150 // Fullname returns the node name that the remote node advertised. 151 func (p *Peer) Fullname() string { 152 return p.rw.name 153 } 154 155 // Caps returns the capabilities (supported subprotocols) of the remote peer. 156 func (p *Peer) Caps() []Cap { 157 // TODO: maybe return copy 158 return p.rw.caps 159 } 160 161 // RemoteAddr returns the remote address of the network connection. 162 func (p *Peer) RemoteAddr() net.Addr { 163 return p.rw.fd.RemoteAddr() 164 } 165 166 // LocalAddr returns the local address of the network connection. 167 func (p *Peer) LocalAddr() net.Addr { 168 return p.rw.fd.LocalAddr() 169 } 170 171 // Disconnect terminates the peer connection with the given reason. 172 // It returns immediately and does not wait until the connection is closed. 173 func (p *Peer) Disconnect(reason DiscReason) { 174 select { 175 case p.disc <- reason: 176 case <-p.closed: 177 } 178 } 179 180 // String implements fmt.Stringer. 181 func (p *Peer) String() string { 182 id := p.ID() 183 return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr()) 184 } 185 186 // Inbound returns true if the peer is an inbound connection 187 func (p *Peer) Inbound() bool { 188 return p.rw.is(inboundConn) 189 } 190 191 func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { 192 protomap := matchProtocols(protocols, conn.caps, conn) 193 p := &Peer{ 194 rw: conn, 195 running: protomap, 196 created: mclock.Now(), 197 disc: make(chan DiscReason), 198 protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop 199 closed: make(chan struct{}), 200 log: log.New("id", conn.node.ID(), "conn", conn.flags), 201 } 202 return p 203 } 204 205 func (p *Peer) Log() log.Logger { 206 return p.log 207 } 208 209 func (p *Peer) run() (remoteRequested bool, err error) { 210 var ( 211 writeStart = make(chan struct{}, 1) 212 writeErr = make(chan error, 1) 213 readErr = make(chan error, 1) 214 reason DiscReason // sent to the peer 215 ) 216 p.wg.Add(2) 217 go p.readLoop(readErr) 218 go p.pingLoop() 219 220 // Start all protocol handlers. 221 writeStart <- struct{}{} 222 p.startProtocols(writeStart, writeErr) 223 224 // Wait for an error or disconnect. 225 loop: 226 for { 227 select { 228 case err = <-writeErr: 229 // A write finished. Allow the next write to start if 230 // there was no error. 231 if err != nil { 232 reason = DiscNetworkError 233 break loop 234 } 235 writeStart <- struct{}{} 236 case err = <-readErr: 237 if r, ok := err.(DiscReason); ok { 238 remoteRequested = true 239 reason = r 240 } else { 241 reason = DiscNetworkError 242 } 243 break loop 244 case err = <-p.protoErr: 245 reason = discReasonForError(err) 246 break loop 247 case err = <-p.disc: 248 reason = discReasonForError(err) 249 break loop 250 } 251 } 252 253 close(p.closed) 254 p.rw.close(reason) 255 p.wg.Wait() 256 return remoteRequested, err 257 } 258 259 func (p *Peer) pingLoop() { 260 ping := time.NewTimer(pingInterval) 261 defer p.wg.Done() 262 defer ping.Stop() 263 for { 264 select { 265 case <-ping.C: 266 if err := SendItems(p.rw, pingMsg); err != nil { 267 p.protoErr <- err 268 return 269 } 270 ping.Reset(pingInterval) 271 case <-p.closed: 272 return 273 } 274 } 275 } 276 277 func (p *Peer) readLoop(errc chan<- error) { 278 defer p.wg.Done() 279 for { 280 msg, err := p.rw.ReadMsg() 281 if err != nil { 282 errc <- err 283 return 284 } 285 msg.ReceivedAt = time.Now() 286 if err = p.handle(msg); err != nil { 287 errc <- err 288 return 289 } 290 } 291 } 292 293 func (p *Peer) handle(msg Msg) error { 294 switch { 295 case msg.Code == pingMsg: 296 msg.Discard() 297 go SendItems(p.rw, pongMsg) 298 case msg.Code == discMsg: 299 // This is the last message. We don't need to discard or 300 // check errors because, the connection will be closed after it. 301 var m struct{ R DiscReason } 302 rlp.Decode(msg.Payload, &m) 303 return m.R 304 case msg.Code < baseProtocolLength: 305 // ignore other base protocol messages 306 return msg.Discard() 307 default: 308 // it's a subprotocol message 309 proto, err := p.getProto(msg.Code) 310 if err != nil { 311 return fmt.Errorf("msg code out of range: %v", msg.Code) 312 } 313 if metrics.Enabled { 314 m := fmt.Sprintf("%s/%s/%d/%#02x", ingressMeterName, proto.Name, proto.Version, msg.Code-proto.offset) 315 metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) 316 metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) 317 } 318 select { 319 case proto.in <- msg: 320 return nil 321 case <-p.closed: 322 return io.EOF 323 } 324 } 325 return nil 326 } 327 328 func countMatchingProtocols(protocols []Protocol, caps []Cap) int { 329 n := 0 330 for _, cap := range caps { 331 for _, proto := range protocols { 332 if proto.Name == cap.Name && proto.Version == cap.Version { 333 n++ 334 } 335 } 336 } 337 return n 338 } 339 340 // matchProtocols creates structures for matching named subprotocols. 341 func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { 342 sort.Sort(capsByNameAndVersion(caps)) 343 offset := baseProtocolLength 344 result := make(map[string]*protoRW) 345 346 outer: 347 for _, cap := range caps { 348 for _, proto := range protocols { 349 if proto.Name == cap.Name && proto.Version == cap.Version { 350 // If an old protocol version matched, revert it 351 if old := result[cap.Name]; old != nil { 352 offset -= old.Length 353 } 354 // Assign the new match 355 result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} 356 offset += proto.Length 357 358 continue outer 359 } 360 } 361 } 362 return result 363 } 364 365 func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { 366 p.wg.Add(len(p.running)) 367 for _, proto := range p.running { 368 proto := proto 369 proto.closed = p.closed 370 proto.wstart = writeStart 371 proto.werr = writeErr 372 var rw MsgReadWriter = proto 373 if p.events != nil { 374 rw = newMsgEventer(rw, p.events, p.ID(), proto.Name, p.Info().Network.RemoteAddress, p.Info().Network.LocalAddress) 375 } 376 p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version)) 377 go func() { 378 defer p.wg.Done() 379 err := proto.Run(p, rw) 380 if err == nil { 381 p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version)) 382 err = errProtocolReturned 383 } else if err != io.EOF { 384 p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err) 385 } 386 p.protoErr <- err 387 }() 388 } 389 } 390 391 // getProto finds the protocol responsible for handling 392 // the given message code. 393 func (p *Peer) getProto(code uint64) (*protoRW, error) { 394 for _, proto := range p.running { 395 if code >= proto.offset && code < proto.offset+proto.Length { 396 return proto, nil 397 } 398 } 399 return nil, newPeerError(errInvalidMsgCode, "%d", code) 400 } 401 402 type protoRW struct { 403 Protocol 404 in chan Msg // receives read messages 405 closed <-chan struct{} // receives when peer is shutting down 406 wstart <-chan struct{} // receives when write may start 407 werr chan<- error // for write results 408 offset uint64 409 w MsgWriter 410 } 411 412 func (rw *protoRW) WriteMsg(msg Msg) (err error) { 413 if msg.Code >= rw.Length { 414 return newPeerError(errInvalidMsgCode, "not handled") 415 } 416 msg.meterCap = rw.cap() 417 msg.meterCode = msg.Code 418 419 msg.Code += rw.offset 420 421 select { 422 case <-rw.wstart: 423 err = rw.w.WriteMsg(msg) 424 // Report write status back to Peer.run. It will initiate 425 // shutdown if the error is non-nil and unblock the next write 426 // otherwise. The calling protocol code should exit for errors 427 // as well but we don't want to rely on that. 428 rw.werr <- err 429 case <-rw.closed: 430 err = ErrShuttingDown 431 } 432 return err 433 } 434 435 func (rw *protoRW) ReadMsg() (Msg, error) { 436 select { 437 case msg := <-rw.in: 438 msg.Code -= rw.offset 439 return msg, nil 440 case <-rw.closed: 441 return Msg{}, io.EOF 442 } 443 } 444 445 // PeerInfo represents a short summary of the information known about a connected 446 // peer. Sub-protocol independent fields are contained and initialized here, with 447 // protocol specifics delegated to all connected sub-protocols. 448 type PeerInfo struct { 449 ENR string `json:"enr,omitempty"` // Core Node Record 450 Enode string `json:"enode"` // Node URL 451 ID string `json:"id"` // Unique node identifier 452 Name string `json:"name"` // Name of the node, including client type, version, OS, custom data 453 Caps []string `json:"caps"` // Protocols advertised by this peer 454 Network struct { 455 LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection 456 RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection 457 Inbound bool `json:"inbound"` 458 Trusted bool `json:"trusted"` 459 Static bool `json:"static"` 460 } `json:"network"` 461 Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields 462 } 463 464 // Info gathers and returns a collection of metadata known about a peer. 465 func (p *Peer) Info() *PeerInfo { 466 // Gather the protocol capabilities 467 var caps []string 468 for _, cap := range p.Caps() { 469 caps = append(caps, cap.String()) 470 } 471 // Assemble the generic peer metadata 472 info := &PeerInfo{ 473 Enode: p.Node().URLv4(), 474 ID: p.ID().String(), 475 Name: p.Fullname(), 476 Caps: caps, 477 Protocols: make(map[string]interface{}), 478 } 479 if p.Node().Seq() > 0 { 480 info.ENR = p.Node().String() 481 } 482 info.Network.LocalAddress = p.LocalAddr().String() 483 info.Network.RemoteAddress = p.RemoteAddr().String() 484 info.Network.Inbound = p.rw.is(inboundConn) 485 info.Network.Trusted = p.rw.is(trustedConn) 486 info.Network.Static = p.rw.is(staticDialedConn) 487 488 // Gather all the running protocol infos 489 for _, proto := range p.running { 490 protoInfo := interface{}("unknown") 491 if query := proto.Protocol.PeerInfo; query != nil { 492 if metadata := query(p.ID()); metadata != nil { 493 protoInfo = metadata 494 } else { 495 protoInfo = "handshake" 496 } 497 } 498 info.Protocols[proto.Name] = protoInfo 499 } 500 return info 501 }