github.com/cryptoecc/eth-ecc@v0.0.3/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/cryptoecc/ETH-ECC/common/mclock" 29 "github.com/cryptoecc/ETH-ECC/event" 30 "github.com/cryptoecc/ETH-ECC/log" 31 "github.com/cryptoecc/ETH-ECC/p2p/enode" 32 "github.com/cryptoecc/ETH-ECC/p2p/enr" 33 "github.com/cryptoecc/ETH-ECC/rlp" 34 ) 35 36 var ( 37 ErrShuttingDown = errors.New("shutting down") 38 ) 39 40 const ( 41 baseProtocolVersion = 5 42 baseProtocolLength = uint64(16) 43 baseProtocolMaxMsgSize = 2 * 1024 44 45 snappyProtocolVersion = 5 46 47 pingInterval = 15 * time.Second 48 ) 49 50 const ( 51 // devp2p message codes 52 handshakeMsg = 0x00 53 discMsg = 0x01 54 pingMsg = 0x02 55 pongMsg = 0x03 56 ) 57 58 // protoHandshake is the RLP structure of the protocol handshake. 59 type protoHandshake struct { 60 Version uint64 61 Name string 62 Caps []Cap 63 ListenPort uint64 64 ID []byte // secp256k1 public key 65 66 // Ignore additional fields (for forward compatibility). 67 Rest []rlp.RawValue `rlp:"tail"` 68 } 69 70 // PeerEventType is the type of peer events emitted by a p2p.Server 71 type PeerEventType string 72 73 const ( 74 // PeerEventTypeAdd is the type of event emitted when a peer is added 75 // to a p2p.Server 76 PeerEventTypeAdd PeerEventType = "add" 77 78 // PeerEventTypeDrop is the type of event emitted when a peer is 79 // dropped from a p2p.Server 80 PeerEventTypeDrop PeerEventType = "drop" 81 82 // PeerEventTypeMsgSend is the type of event emitted when a 83 // message is successfully sent to a peer 84 PeerEventTypeMsgSend PeerEventType = "msgsend" 85 86 // PeerEventTypeMsgRecv is the type of event emitted when a 87 // message is received from a peer 88 PeerEventTypeMsgRecv PeerEventType = "msgrecv" 89 ) 90 91 // PeerEvent is an event emitted when peers are either added or dropped from 92 // a p2p.Server or when a message is sent or received on a peer connection 93 type PeerEvent struct { 94 Type PeerEventType `json:"type"` 95 Peer enode.ID `json:"peer"` 96 Error string `json:"error,omitempty"` 97 Protocol string `json:"protocol,omitempty"` 98 MsgCode *uint64 `json:"msg_code,omitempty"` 99 MsgSize *uint32 `json:"msg_size,omitempty"` 100 LocalAddress string `json:"local,omitempty"` 101 RemoteAddress string `json:"remote,omitempty"` 102 } 103 104 // Peer represents a connected remote node. 105 type Peer struct { 106 rw *conn 107 running map[string]*protoRW 108 log log.Logger 109 created mclock.AbsTime 110 111 wg sync.WaitGroup 112 protoErr chan error 113 closed chan struct{} 114 disc chan DiscReason 115 116 // events receives message send / receive events if set 117 events *event.Feed 118 } 119 120 // NewPeer returns a peer for testing purposes. 121 func NewPeer(id enode.ID, name string, caps []Cap) *Peer { 122 pipe, _ := net.Pipe() 123 node := enode.SignNull(new(enr.Record), id) 124 conn := &conn{fd: pipe, transport: nil, node: node, caps: caps, name: name} 125 peer := newPeer(log.Root(), conn, nil) 126 close(peer.closed) // ensures Disconnect doesn't block 127 return peer 128 } 129 130 // ID returns the node's public key. 131 func (p *Peer) ID() enode.ID { 132 return p.rw.node.ID() 133 } 134 135 // Node returns the peer's node descriptor. 136 func (p *Peer) Node() *enode.Node { 137 return p.rw.node 138 } 139 140 // Name returns the node name that the remote node advertised. 141 func (p *Peer) Name() string { 142 return p.rw.name 143 } 144 145 // Caps returns the capabilities (supported subprotocols) of the remote peer. 146 func (p *Peer) Caps() []Cap { 147 // TODO: maybe return copy 148 return p.rw.caps 149 } 150 151 // RemoteAddr returns the remote address of the network connection. 152 func (p *Peer) RemoteAddr() net.Addr { 153 return p.rw.fd.RemoteAddr() 154 } 155 156 // LocalAddr returns the local address of the network connection. 157 func (p *Peer) LocalAddr() net.Addr { 158 return p.rw.fd.LocalAddr() 159 } 160 161 // Disconnect terminates the peer connection with the given reason. 162 // It returns immediately and does not wait until the connection is closed. 163 func (p *Peer) Disconnect(reason DiscReason) { 164 select { 165 case p.disc <- reason: 166 case <-p.closed: 167 } 168 } 169 170 // String implements fmt.Stringer. 171 func (p *Peer) String() string { 172 id := p.ID() 173 return fmt.Sprintf("Peer %x %v", id[:8], p.RemoteAddr()) 174 } 175 176 // Inbound returns true if the peer is an inbound connection 177 func (p *Peer) Inbound() bool { 178 return p.rw.is(inboundConn) 179 } 180 181 func newPeer(log log.Logger, conn *conn, protocols []Protocol) *Peer { 182 protomap := matchProtocols(protocols, conn.caps, conn) 183 p := &Peer{ 184 rw: conn, 185 running: protomap, 186 created: mclock.Now(), 187 disc: make(chan DiscReason), 188 protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop 189 closed: make(chan struct{}), 190 log: log.New("id", conn.node.ID(), "conn", conn.flags), 191 } 192 return p 193 } 194 195 func (p *Peer) Log() log.Logger { 196 return p.log 197 } 198 199 func (p *Peer) run() (remoteRequested bool, err error) { 200 var ( 201 writeStart = make(chan struct{}, 1) 202 writeErr = make(chan error, 1) 203 readErr = make(chan error, 1) 204 reason DiscReason // sent to the peer 205 ) 206 p.wg.Add(2) 207 go p.readLoop(readErr) 208 go p.pingLoop() 209 210 // Start all protocol handlers. 211 writeStart <- struct{}{} 212 p.startProtocols(writeStart, writeErr) 213 214 // Wait for an error or disconnect. 215 loop: 216 for { 217 select { 218 case err = <-writeErr: 219 // A write finished. Allow the next write to start if 220 // there was no error. 221 if err != nil { 222 reason = DiscNetworkError 223 break loop 224 } 225 writeStart <- struct{}{} 226 case err = <-readErr: 227 if r, ok := err.(DiscReason); ok { 228 remoteRequested = true 229 reason = r 230 } else { 231 reason = DiscNetworkError 232 } 233 break loop 234 case err = <-p.protoErr: 235 reason = discReasonForError(err) 236 break loop 237 case err = <-p.disc: 238 reason = discReasonForError(err) 239 break loop 240 } 241 } 242 243 close(p.closed) 244 p.rw.close(reason) 245 p.wg.Wait() 246 return remoteRequested, err 247 } 248 249 func (p *Peer) pingLoop() { 250 ping := time.NewTimer(pingInterval) 251 defer p.wg.Done() 252 defer ping.Stop() 253 for { 254 select { 255 case <-ping.C: 256 if err := SendItems(p.rw, pingMsg); err != nil { 257 p.protoErr <- err 258 return 259 } 260 ping.Reset(pingInterval) 261 case <-p.closed: 262 return 263 } 264 } 265 } 266 267 func (p *Peer) readLoop(errc chan<- error) { 268 defer p.wg.Done() 269 for { 270 msg, err := p.rw.ReadMsg() 271 if err != nil { 272 errc <- err 273 return 274 } 275 msg.ReceivedAt = time.Now() 276 if err = p.handle(msg); err != nil { 277 errc <- err 278 return 279 } 280 } 281 } 282 283 func (p *Peer) handle(msg Msg) error { 284 switch { 285 case msg.Code == pingMsg: 286 msg.Discard() 287 go SendItems(p.rw, pongMsg) 288 case msg.Code == discMsg: 289 var reason [1]DiscReason 290 // This is the last message. We don't need to discard or 291 // check errors because, the connection will be closed after it. 292 rlp.Decode(msg.Payload, &reason) 293 return reason[0] 294 case msg.Code < baseProtocolLength: 295 // ignore other base protocol messages 296 return msg.Discard() 297 default: 298 // it's a subprotocol message 299 proto, err := p.getProto(msg.Code) 300 if err != nil { 301 return fmt.Errorf("msg code out of range: %v", msg.Code) 302 } 303 select { 304 case proto.in <- msg: 305 return nil 306 case <-p.closed: 307 return io.EOF 308 } 309 } 310 return nil 311 } 312 313 func countMatchingProtocols(protocols []Protocol, caps []Cap) int { 314 n := 0 315 for _, cap := range caps { 316 for _, proto := range protocols { 317 if proto.Name == cap.Name && proto.Version == cap.Version { 318 n++ 319 } 320 } 321 } 322 return n 323 } 324 325 // matchProtocols creates structures for matching named subprotocols. 326 func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { 327 sort.Sort(capsByNameAndVersion(caps)) 328 offset := baseProtocolLength 329 result := make(map[string]*protoRW) 330 331 outer: 332 for _, cap := range caps { 333 for _, proto := range protocols { 334 if proto.Name == cap.Name && proto.Version == cap.Version { 335 // If an old protocol version matched, revert it 336 if old := result[cap.Name]; old != nil { 337 offset -= old.Length 338 } 339 // Assign the new match 340 result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} 341 offset += proto.Length 342 343 continue outer 344 } 345 } 346 } 347 return result 348 } 349 350 func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { 351 p.wg.Add(len(p.running)) 352 for _, proto := range p.running { 353 proto := proto 354 proto.closed = p.closed 355 proto.wstart = writeStart 356 proto.werr = writeErr 357 var rw MsgReadWriter = proto 358 if p.events != nil { 359 rw = newMsgEventer(rw, p.events, p.ID(), proto.Name, p.Info().Network.RemoteAddress, p.Info().Network.LocalAddress) 360 } 361 p.log.Trace(fmt.Sprintf("Starting protocol %s/%d", proto.Name, proto.Version)) 362 go func() { 363 err := proto.Run(p, rw) 364 if err == nil { 365 p.log.Trace(fmt.Sprintf("Protocol %s/%d returned", proto.Name, proto.Version)) 366 err = errProtocolReturned 367 } else if err != io.EOF { 368 p.log.Trace(fmt.Sprintf("Protocol %s/%d failed", proto.Name, proto.Version), "err", err) 369 } 370 p.protoErr <- err 371 p.wg.Done() 372 }() 373 } 374 } 375 376 // getProto finds the protocol responsible for handling 377 // the given message code. 378 func (p *Peer) getProto(code uint64) (*protoRW, error) { 379 for _, proto := range p.running { 380 if code >= proto.offset && code < proto.offset+proto.Length { 381 return proto, nil 382 } 383 } 384 return nil, newPeerError(errInvalidMsgCode, "%d", code) 385 } 386 387 type protoRW struct { 388 Protocol 389 in chan Msg // receives read messages 390 closed <-chan struct{} // receives when peer is shutting down 391 wstart <-chan struct{} // receives when write may start 392 werr chan<- error // for write results 393 offset uint64 394 w MsgWriter 395 } 396 397 func (rw *protoRW) WriteMsg(msg Msg) (err error) { 398 if msg.Code >= rw.Length { 399 return newPeerError(errInvalidMsgCode, "not handled") 400 } 401 msg.Code += rw.offset 402 select { 403 case <-rw.wstart: 404 err = rw.w.WriteMsg(msg) 405 // Report write status back to Peer.run. It will initiate 406 // shutdown if the error is non-nil and unblock the next write 407 // otherwise. The calling protocol code should exit for errors 408 // as well but we don't want to rely on that. 409 rw.werr <- err 410 case <-rw.closed: 411 err = ErrShuttingDown 412 } 413 return err 414 } 415 416 func (rw *protoRW) ReadMsg() (Msg, error) { 417 select { 418 case msg := <-rw.in: 419 msg.Code -= rw.offset 420 return msg, nil 421 case <-rw.closed: 422 return Msg{}, io.EOF 423 } 424 } 425 426 // PeerInfo represents a short summary of the information known about a connected 427 // peer. Sub-protocol independent fields are contained and initialized here, with 428 // protocol specifics delegated to all connected sub-protocols. 429 type PeerInfo struct { 430 ENR string `json:"enr,omitempty"` // Ethereum Node Record 431 Enode string `json:"enode"` // Node URL 432 ID string `json:"id"` // Unique node identifier 433 Name string `json:"name"` // Name of the node, including client type, version, OS, custom data 434 Caps []string `json:"caps"` // Protocols advertised by this peer 435 Network struct { 436 LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection 437 RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection 438 Inbound bool `json:"inbound"` 439 Trusted bool `json:"trusted"` 440 Static bool `json:"static"` 441 } `json:"network"` 442 Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields 443 } 444 445 // Info gathers and returns a collection of metadata known about a peer. 446 func (p *Peer) Info() *PeerInfo { 447 // Gather the protocol capabilities 448 var caps []string 449 for _, cap := range p.Caps() { 450 caps = append(caps, cap.String()) 451 } 452 // Assemble the generic peer metadata 453 info := &PeerInfo{ 454 Enode: p.Node().URLv4(), 455 ID: p.ID().String(), 456 Name: p.Name(), 457 Caps: caps, 458 Protocols: make(map[string]interface{}), 459 } 460 if p.Node().Seq() > 0 { 461 info.ENR = p.Node().String() 462 } 463 info.Network.LocalAddress = p.LocalAddr().String() 464 info.Network.RemoteAddress = p.RemoteAddr().String() 465 info.Network.Inbound = p.rw.is(inboundConn) 466 info.Network.Trusted = p.rw.is(trustedConn) 467 info.Network.Static = p.rw.is(staticDialedConn) 468 469 // Gather all the running protocol infos 470 for _, proto := range p.running { 471 protoInfo := interface{}("unknown") 472 if query := proto.Protocol.PeerInfo; query != nil { 473 if metadata := query(p.ID()); metadata != nil { 474 protoInfo = metadata 475 } else { 476 protoInfo = "handshake" 477 } 478 } 479 info.Protocols[proto.Name] = protoInfo 480 } 481 return info 482 }