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