github.com/number571/tendermint@v0.34.11-gost/internal/p2p/pex/reactor.go (about) 1 package pex 2 3 import ( 4 "context" 5 "fmt" 6 "runtime/debug" 7 "sync" 8 "time" 9 10 "github.com/number571/tendermint/internal/p2p" 11 "github.com/number571/tendermint/internal/p2p/conn" 12 "github.com/number571/tendermint/libs/log" 13 tmmath "github.com/number571/tendermint/libs/math" 14 "github.com/number571/tendermint/libs/service" 15 protop2p "github.com/number571/tendermint/proto/tendermint/p2p" 16 "github.com/number571/tendermint/types" 17 ) 18 19 var ( 20 _ service.Service = (*ReactorV2)(nil) 21 _ p2p.Wrapper = (*protop2p.PexMessage)(nil) 22 ) 23 24 // TODO: Consolidate with params file. 25 // See https://github.com/number571/tendermint/issues/6371 26 const ( 27 // the minimum time one peer can send another request to the same peer 28 minReceiveRequestInterval = 100 * time.Millisecond 29 30 // the maximum amount of addresses that can be included in a response 31 maxAddresses uint16 = 100 32 33 // allocated time to resolve a node address into a set of endpoints 34 resolveTimeout = 3 * time.Second 35 36 // How long to wait when there are no peers available before trying again 37 noAvailablePeersWaitPeriod = 1 * time.Second 38 39 // indicates the ping rate of the pex reactor when the peer store is full. 40 // The reactor should still look to add new peers in order to flush out low 41 // scoring peers that are still in the peer store 42 fullCapacityInterval = 10 * time.Minute 43 ) 44 45 // TODO: We should decide whether we want channel descriptors to be housed 46 // within each reactor (as they are now) or, considering that the reactor doesn't 47 // really need to care about the channel descriptors, if they should be housed 48 // in the node module. 49 func ChannelDescriptor() conn.ChannelDescriptor { 50 return conn.ChannelDescriptor{ 51 ID: PexChannel, 52 Priority: 1, 53 SendQueueCapacity: 10, 54 RecvMessageCapacity: maxMsgSize, 55 RecvBufferCapacity: 32, 56 MaxSendBytes: 200, 57 } 58 } 59 60 // ReactorV2 is a PEX reactor for the new P2P stack. The legacy reactor 61 // is Reactor. 62 // 63 // FIXME: Rename this when Reactor is removed, and consider moving to p2p/. 64 // 65 // The peer exchange or PEX reactor supports the peer manager by sending 66 // requests to other peers for addresses that can be given to the peer manager 67 // and at the same time advertises addresses to peers that need more. 68 // 69 // The reactor is able to tweak the intensity of it's search by decreasing or 70 // increasing the interval between each request. It tracks connected peers via 71 // a linked list, sending a request to the node at the front of the list and 72 // adding it to the back of the list once a response is received. 73 type ReactorV2 struct { 74 service.BaseService 75 76 peerManager *p2p.PeerManager 77 pexCh *p2p.Channel 78 peerUpdates *p2p.PeerUpdates 79 closeCh chan struct{} 80 81 // list of available peers to loop through and send peer requests to 82 availablePeers map[types.NodeID]struct{} 83 84 mtx sync.RWMutex 85 86 // requestsSent keeps track of which peers the PEX reactor has sent requests 87 // to. This prevents the sending of spurious responses. 88 // NOTE: If a node never responds, they will remain in this map until a 89 // peer down status update is sent 90 requestsSent map[types.NodeID]struct{} 91 92 // lastReceivedRequests keeps track of when peers send a request to prevent 93 // peers from sending requests too often (as defined by 94 // minReceiveRequestInterval). 95 lastReceivedRequests map[types.NodeID]time.Time 96 97 // the time when another request will be sent 98 nextRequestTime time.Time 99 100 // keep track of how many new peers to existing peers we have received to 101 // extrapolate the size of the network 102 newPeers uint32 103 totalPeers uint32 104 105 // discoveryRatio is the inverse ratio of new peers to old peers squared. 106 // This is multiplied by the minimum duration to calculate how long to wait 107 // between each request. 108 discoveryRatio float32 109 } 110 111 // NewReactor returns a reference to a new reactor. 112 func NewReactorV2( 113 logger log.Logger, 114 peerManager *p2p.PeerManager, 115 pexCh *p2p.Channel, 116 peerUpdates *p2p.PeerUpdates, 117 ) *ReactorV2 { 118 119 r := &ReactorV2{ 120 peerManager: peerManager, 121 pexCh: pexCh, 122 peerUpdates: peerUpdates, 123 closeCh: make(chan struct{}), 124 availablePeers: make(map[types.NodeID]struct{}), 125 requestsSent: make(map[types.NodeID]struct{}), 126 lastReceivedRequests: make(map[types.NodeID]time.Time), 127 } 128 129 r.BaseService = *service.NewBaseService(logger, "PEX", r) 130 return r 131 } 132 133 // OnStart starts separate go routines for each p2p Channel and listens for 134 // envelopes on each. In addition, it also listens for peer updates and handles 135 // messages on that p2p channel accordingly. The caller must be sure to execute 136 // OnStop to ensure the outbound p2p Channels are closed. 137 func (r *ReactorV2) OnStart() error { 138 go r.processPexCh() 139 go r.processPeerUpdates() 140 return nil 141 } 142 143 // OnStop stops the reactor by signaling to all spawned goroutines to exit and 144 // blocking until they all exit. 145 func (r *ReactorV2) OnStop() { 146 // Close closeCh to signal to all spawned goroutines to gracefully exit. All 147 // p2p Channels should execute Close(). 148 close(r.closeCh) 149 150 // Wait for all p2p Channels to be closed before returning. This ensures we 151 // can easily reason about synchronization of all p2p Channels and ensure no 152 // panics will occur. 153 <-r.pexCh.Done() 154 <-r.peerUpdates.Done() 155 } 156 157 // processPexCh implements a blocking event loop where we listen for p2p 158 // Envelope messages from the pexCh. 159 func (r *ReactorV2) processPexCh() { 160 defer r.pexCh.Close() 161 162 for { 163 select { 164 case <-r.closeCh: 165 r.Logger.Debug("stopped listening on PEX channel; closing...") 166 return 167 168 // outbound requests for new peers 169 case <-r.waitUntilNextRequest(): 170 r.sendRequestForPeers() 171 172 // inbound requests for new peers or responses to requests sent by this 173 // reactor 174 case envelope := <-r.pexCh.In: 175 if err := r.handleMessage(r.pexCh.ID, envelope); err != nil { 176 r.Logger.Error("failed to process message", "ch_id", r.pexCh.ID, "envelope", envelope, "err", err) 177 r.pexCh.Error <- p2p.PeerError{ 178 NodeID: envelope.From, 179 Err: err, 180 } 181 } 182 } 183 } 184 } 185 186 // processPeerUpdates initiates a blocking process where we listen for and handle 187 // PeerUpdate messages. When the reactor is stopped, we will catch the signal and 188 // close the p2p PeerUpdatesCh gracefully. 189 func (r *ReactorV2) processPeerUpdates() { 190 defer r.peerUpdates.Close() 191 192 for { 193 select { 194 case peerUpdate := <-r.peerUpdates.Updates(): 195 r.processPeerUpdate(peerUpdate) 196 197 case <-r.closeCh: 198 r.Logger.Debug("stopped listening on peer updates channel; closing...") 199 return 200 } 201 } 202 } 203 204 // handlePexMessage handles envelopes sent from peers on the PexChannel. 205 func (r *ReactorV2) handlePexMessage(envelope p2p.Envelope) error { 206 logger := r.Logger.With("peer", envelope.From) 207 208 switch msg := envelope.Message.(type) { 209 210 case *protop2p.PexRequest: 211 // Check if the peer hasn't sent a prior request too close to this one 212 // in time. 213 if err := r.markPeerRequest(envelope.From); err != nil { 214 return err 215 } 216 217 // parse and send the legacy PEX addresses 218 pexAddresses := r.resolve(r.peerManager.Advertise(envelope.From, maxAddresses)) 219 r.pexCh.Out <- p2p.Envelope{ 220 To: envelope.From, 221 Message: &protop2p.PexResponse{Addresses: pexAddresses}, 222 } 223 224 case *protop2p.PexResponse: 225 // check if the response matches a request that was made to that peer 226 if err := r.markPeerResponse(envelope.From); err != nil { 227 return err 228 } 229 230 // check the size of the response 231 if len(msg.Addresses) > int(maxAddresses) { 232 return fmt.Errorf("peer sent too many addresses (max: %d, got: %d)", 233 maxAddresses, 234 len(msg.Addresses), 235 ) 236 } 237 238 for _, pexAddress := range msg.Addresses { 239 // no protocol is prefixed so we assume the default (mconn) 240 peerAddress, err := p2p.ParseNodeAddress( 241 fmt.Sprintf("%s@%s:%d", pexAddress.ID, pexAddress.IP, pexAddress.Port)) 242 if err != nil { 243 continue 244 } 245 added, err := r.peerManager.Add(peerAddress) 246 if err != nil { 247 logger.Error("failed to add PEX address", "address", peerAddress, "err", err) 248 } 249 if added { 250 r.newPeers++ 251 logger.Debug("added PEX address", "address", peerAddress) 252 } 253 r.totalPeers++ 254 } 255 256 // V2 PEX MESSAGES 257 case *protop2p.PexRequestV2: 258 // check if the peer hasn't sent a prior request too close to this one 259 // in time 260 if err := r.markPeerRequest(envelope.From); err != nil { 261 return err 262 } 263 264 // request peers from the peer manager and parse the NodeAddresses into 265 // URL strings 266 nodeAddresses := r.peerManager.Advertise(envelope.From, maxAddresses) 267 pexAddressesV2 := make([]protop2p.PexAddressV2, len(nodeAddresses)) 268 for idx, addr := range nodeAddresses { 269 pexAddressesV2[idx] = protop2p.PexAddressV2{ 270 URL: addr.String(), 271 } 272 } 273 r.pexCh.Out <- p2p.Envelope{ 274 To: envelope.From, 275 Message: &protop2p.PexResponseV2{Addresses: pexAddressesV2}, 276 } 277 278 case *protop2p.PexResponseV2: 279 // check if the response matches a request that was made to that peer 280 if err := r.markPeerResponse(envelope.From); err != nil { 281 return err 282 } 283 284 // check the size of the response 285 if len(msg.Addresses) > int(maxAddresses) { 286 return fmt.Errorf("peer sent too many addresses (max: %d, got: %d)", 287 maxAddresses, 288 len(msg.Addresses), 289 ) 290 } 291 292 for _, pexAddress := range msg.Addresses { 293 peerAddress, err := p2p.ParseNodeAddress(pexAddress.URL) 294 if err != nil { 295 continue 296 } 297 added, err := r.peerManager.Add(peerAddress) 298 if err != nil { 299 logger.Error("failed to add V2 PEX address", "address", peerAddress, "err", err) 300 } 301 if added { 302 r.newPeers++ 303 logger.Debug("added V2 PEX address", "address", peerAddress) 304 } 305 r.totalPeers++ 306 } 307 308 default: 309 return fmt.Errorf("received unknown message: %T", msg) 310 } 311 312 return nil 313 } 314 315 // resolve resolves a set of peer addresses into PEX addresses. 316 // 317 // FIXME: This is necessary because the current PEX protocol only supports 318 // IP/port pairs, while the P2P stack uses NodeAddress URLs. The PEX protocol 319 // should really use URLs too, to exchange DNS names instead of IPs and allow 320 // different transport protocols (e.g. QUIC and MemoryTransport). 321 // 322 // FIXME: We may want to cache and parallelize this, but for now we'll just rely 323 // on the operating system to cache it for us. 324 func (r *ReactorV2) resolve(addresses []p2p.NodeAddress) []protop2p.PexAddress { 325 limit := len(addresses) 326 pexAddresses := make([]protop2p.PexAddress, 0, limit) 327 328 for _, address := range addresses { 329 ctx, cancel := context.WithTimeout(context.Background(), resolveTimeout) 330 endpoints, err := address.Resolve(ctx) 331 r.Logger.Debug("resolved node address", "endpoints", endpoints) 332 cancel() 333 334 if err != nil { 335 r.Logger.Debug("failed to resolve address", "address", address, "err", err) 336 continue 337 } 338 339 for _, endpoint := range endpoints { 340 r.Logger.Debug("checking endpint", "IP", endpoint.IP, "Port", endpoint.Port) 341 if len(pexAddresses) >= limit { 342 return pexAddresses 343 344 } else if endpoint.IP != nil { 345 r.Logger.Debug("appending pex address") 346 // PEX currently only supports IP-networked transports (as 347 // opposed to e.g. p2p.MemoryTransport). 348 // 349 // FIXME: as the PEX address contains no information about the 350 // protocol, we jam this into the ID. We won't need to this once 351 // we support URLs 352 pexAddresses = append(pexAddresses, protop2p.PexAddress{ 353 ID: string(address.NodeID), 354 IP: endpoint.IP.String(), 355 Port: uint32(endpoint.Port), 356 }) 357 } 358 } 359 } 360 361 return pexAddresses 362 } 363 364 // handleMessage handles an Envelope sent from a peer on a specific p2p Channel. 365 // It will handle errors and any possible panics gracefully. A caller can handle 366 // any error returned by sending a PeerError on the respective channel. 367 func (r *ReactorV2) handleMessage(chID p2p.ChannelID, envelope p2p.Envelope) (err error) { 368 defer func() { 369 if e := recover(); e != nil { 370 err = fmt.Errorf("panic in processing message: %v", e) 371 r.Logger.Error( 372 "recovering from processing message panic", 373 "err", err, 374 "stack", string(debug.Stack()), 375 ) 376 } 377 }() 378 379 r.Logger.Debug("received PEX message", "peer", envelope.From) 380 381 switch chID { 382 case p2p.ChannelID(PexChannel): 383 err = r.handlePexMessage(envelope) 384 385 default: 386 err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope) 387 } 388 389 return err 390 } 391 392 // processPeerUpdate processes a PeerUpdate. For added peers, PeerStatusUp, we 393 // send a request for addresses. 394 func (r *ReactorV2) processPeerUpdate(peerUpdate p2p.PeerUpdate) { 395 r.Logger.Debug("received PEX peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) 396 397 r.mtx.Lock() 398 defer r.mtx.Unlock() 399 400 switch peerUpdate.Status { 401 case p2p.PeerStatusUp: 402 r.availablePeers[peerUpdate.NodeID] = struct{}{} 403 case p2p.PeerStatusDown: 404 delete(r.availablePeers, peerUpdate.NodeID) 405 delete(r.requestsSent, peerUpdate.NodeID) 406 delete(r.lastReceivedRequests, peerUpdate.NodeID) 407 default: 408 } 409 } 410 411 func (r *ReactorV2) waitUntilNextRequest() <-chan time.Time { 412 return time.After(time.Until(r.nextRequestTime)) 413 } 414 415 // sendRequestForPeers pops the first peerID off the list and sends the 416 // peer a request for more peer addresses. The function then moves the 417 // peer into the requestsSent bucket and calculates when the next request 418 // time should be 419 func (r *ReactorV2) sendRequestForPeers() { 420 r.mtx.Lock() 421 defer r.mtx.Unlock() 422 if len(r.availablePeers) == 0 { 423 // no peers are available 424 r.Logger.Debug("no available peers to send request to, waiting...") 425 r.nextRequestTime = time.Now().Add(noAvailablePeersWaitPeriod) 426 427 return 428 } 429 var peerID types.NodeID 430 431 // use range to get a random peer. 432 for peerID = range r.availablePeers { 433 break 434 } 435 436 // The node accommodates for both pex systems 437 if r.isLegacyPeer(peerID) { 438 r.pexCh.Out <- p2p.Envelope{ 439 To: peerID, 440 Message: &protop2p.PexRequest{}, 441 } 442 } else { 443 r.pexCh.Out <- p2p.Envelope{ 444 To: peerID, 445 Message: &protop2p.PexRequestV2{}, 446 } 447 } 448 449 // remove the peer from the abvailable peers list and mark it in the requestsSent map 450 delete(r.availablePeers, peerID) 451 r.requestsSent[peerID] = struct{}{} 452 453 r.calculateNextRequestTime() 454 r.Logger.Debug("peer request sent", "next_request_time", r.nextRequestTime) 455 } 456 457 // calculateNextRequestTime implements something of a proportional controller 458 // to estimate how often the reactor should be requesting new peer addresses. 459 // The dependent variable in this calculation is the ratio of new peers to 460 // all peers that the reactor receives. The interval is thus calculated as the 461 // inverse squared. In the beginning, all peers should be new peers. 462 // We expect this ratio to be near 1 and thus the interval to be as short 463 // as possible. As the node becomes more familiar with the network the ratio of 464 // new nodes will plummet to a very small number, meaning the interval expands 465 // to its upper bound. 466 // CONTRACT: Must use a write lock as nextRequestTime is updated 467 func (r *ReactorV2) calculateNextRequestTime() { 468 // check if the peer store is full. If so then there is no need 469 // to send peer requests too often 470 if ratio := r.peerManager.PeerRatio(); ratio >= 0.95 { 471 r.Logger.Debug("peer manager near full ratio, sleeping...", 472 "sleep_period", fullCapacityInterval, "ratio", ratio) 473 r.nextRequestTime = time.Now().Add(fullCapacityInterval) 474 return 475 } 476 477 // baseTime represents the shortest interval that we can send peer requests 478 // in. For example if we have 10 peers and we can't send a message to the 479 // same peer every 500ms, then we can send a request every 50ms. In practice 480 // we use a safety margin of 2, ergo 100ms 481 peers := tmmath.MinInt(len(r.availablePeers), 50) 482 baseTime := minReceiveRequestInterval 483 if peers > 0 { 484 baseTime = minReceiveRequestInterval * 2 / time.Duration(peers) 485 } 486 487 if r.totalPeers > 0 || r.discoveryRatio == 0 { 488 // find the ratio of new peers. NOTE: We add 1 to both sides to avoid 489 // divide by zero problems 490 ratio := float32(r.totalPeers+1) / float32(r.newPeers+1) 491 // square the ratio in order to get non linear time intervals 492 // NOTE: The longest possible interval for a network with 100 or more peers 493 // where a node is connected to 50 of them is 2 minutes. 494 r.discoveryRatio = ratio * ratio 495 r.newPeers = 0 496 r.totalPeers = 0 497 } 498 // NOTE: As ratio is always >= 1, discovery ratio is >= 1. Therefore we don't need to worry 499 // about the next request time being less than the minimum time 500 r.nextRequestTime = time.Now().Add(baseTime * time.Duration(r.discoveryRatio)) 501 } 502 503 func (r *ReactorV2) markPeerRequest(peer types.NodeID) error { 504 r.mtx.Lock() 505 defer r.mtx.Unlock() 506 if lastRequestTime, ok := r.lastReceivedRequests[peer]; ok { 507 if time.Now().Before(lastRequestTime.Add(minReceiveRequestInterval)) { 508 return fmt.Errorf("peer sent a request too close after a prior one. Minimum interval: %v", 509 minReceiveRequestInterval) 510 } 511 } 512 r.lastReceivedRequests[peer] = time.Now() 513 return nil 514 } 515 516 func (r *ReactorV2) markPeerResponse(peer types.NodeID) error { 517 r.mtx.Lock() 518 defer r.mtx.Unlock() 519 // check if a request to this peer was sent 520 if _, ok := r.requestsSent[peer]; !ok { 521 return fmt.Errorf("peer sent a PEX response when none was requested (%v)", peer) 522 } 523 delete(r.requestsSent, peer) 524 // attach to the back of the list so that the peer can be used again for 525 // future requests 526 527 r.availablePeers[peer] = struct{}{} 528 return nil 529 } 530 531 // all addresses must use a MCONN protocol for the peer to be considered part of the 532 // legacy p2p pex system 533 func (r *ReactorV2) isLegacyPeer(peer types.NodeID) bool { 534 for _, addr := range r.peerManager.Addresses(peer) { 535 if addr.Protocol != p2p.MConnProtocol { 536 return false 537 } 538 } 539 return true 540 }