github.com/decred/dcrlnd@v0.7.6/discovery/syncer.go (about) 1 package discovery 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "math/rand" 8 "sort" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrlnd/lnpeer" 15 "github.com/decred/dcrlnd/lnwire" 16 "golang.org/x/time/rate" 17 ) 18 19 // SyncerType encapsulates the different types of syncing mechanisms for a 20 // gossip syncer. 21 type SyncerType uint8 22 23 const ( 24 // ActiveSync denotes that a gossip syncer: 25 // 26 // 1. Should not attempt to synchronize with the remote peer for 27 // missing channels. 28 // 2. Should respond to queries from the remote peer. 29 // 3. Should receive new updates from the remote peer. 30 // 31 // They are started in a chansSynced state in order to accomplish their 32 // responsibilities above. 33 ActiveSync SyncerType = iota 34 35 // PassiveSync denotes that a gossip syncer: 36 // 37 // 1. Should not attempt to synchronize with the remote peer for 38 // missing channels. 39 // 2. Should respond to queries from the remote peer. 40 // 3. Should not receive new updates from the remote peer. 41 // 42 // They are started in a chansSynced state in order to accomplish their 43 // responsibilities above. 44 PassiveSync 45 46 // PinnedSync denotes an ActiveSync that doesn't count towards the 47 // default active syncer limits and is always active throughout the 48 // duration of the peer's connection. Each pinned syncer will begin by 49 // performing a historical sync to ensure we are well synchronized with 50 // their routing table. 51 PinnedSync 52 ) 53 54 // String returns a human readable string describing the target SyncerType. 55 func (t SyncerType) String() string { 56 switch t { 57 case ActiveSync: 58 return "ActiveSync" 59 case PassiveSync: 60 return "PassiveSync" 61 case PinnedSync: 62 return "PinnedSync" 63 default: 64 return fmt.Sprintf("unknown sync type %d", t) 65 } 66 } 67 68 // IsActiveSync returns true if the SyncerType should set a GossipTimestampRange 69 // allowing new gossip messages to be received from the peer. 70 func (t SyncerType) IsActiveSync() bool { 71 switch t { 72 case ActiveSync, PinnedSync: 73 return true 74 default: 75 return false 76 } 77 } 78 79 // syncerState is an enum that represents the current state of the GossipSyncer. 80 // As the syncer is a state machine, we'll gate our actions based off of the 81 // current state and the next incoming message. 82 type syncerState uint32 83 84 const ( 85 // syncingChans is the default state of the GossipSyncer. We start in 86 // this state when a new peer first connects and we don't yet know if 87 // we're fully synchronized. 88 syncingChans syncerState = iota 89 90 // waitingQueryRangeReply is the second main phase of the GossipSyncer. 91 // We enter this state after we send out our first QueryChannelRange 92 // reply. We'll stay in this state until the remote party sends us a 93 // ReplyShortChanIDsEnd message that indicates they've responded to our 94 // query entirely. After this state, we'll transition to 95 // waitingQueryChanReply after we send out requests for all the new 96 // chan ID's to us. 97 waitingQueryRangeReply 98 99 // queryNewChannels is the third main phase of the GossipSyncer. In 100 // this phase we'll send out all of our QueryShortChanIDs messages in 101 // response to the new channels that we don't yet know about. 102 queryNewChannels 103 104 // waitingQueryChanReply is the fourth main phase of the GossipSyncer. 105 // We enter this phase once we've sent off a query chink to the remote 106 // peer. We'll stay in this phase until we receive a 107 // ReplyShortChanIDsEnd message which indicates that the remote party 108 // has responded to all of our requests. 109 waitingQueryChanReply 110 111 // chansSynced is the terminal stage of the GossipSyncer. Once we enter 112 // this phase, we'll send out our update horizon, which filters out the 113 // set of channel updates that we're interested in. In this state, 114 // we'll be able to accept any outgoing messages from the 115 // AuthenticatedGossiper, and decide if we should forward them to our 116 // target peer based on its update horizon. 117 chansSynced 118 119 // syncerIdle is a state in which the gossip syncer can handle external 120 // requests to transition or perform historical syncs. It is used as the 121 // initial state for pinned syncers, as well as a fallthrough case for 122 // chansSynced allowing fully synced peers to facilitate requests. 123 syncerIdle 124 ) 125 126 // String returns a human readable string describing the target syncerState. 127 func (s syncerState) String() string { 128 switch s { 129 case syncingChans: 130 return "syncingChans" 131 132 case waitingQueryRangeReply: 133 return "waitingQueryRangeReply" 134 135 case queryNewChannels: 136 return "queryNewChannels" 137 138 case waitingQueryChanReply: 139 return "waitingQueryChanReply" 140 141 case chansSynced: 142 return "chansSynced" 143 144 case syncerIdle: 145 return "syncerIdle" 146 147 default: 148 return "UNKNOWN STATE" 149 } 150 } 151 152 const ( 153 // DefaultMaxUndelayedQueryReplies specifies how many gossip queries we 154 // will respond to immediately before starting to delay responses. 155 DefaultMaxUndelayedQueryReplies = 10 156 157 // DefaultDelayedQueryReplyInterval is the length of time we will wait 158 // before responding to gossip queries after replying to 159 // maxUndelayedQueryReplies queries. 160 DefaultDelayedQueryReplyInterval = 5 * time.Second 161 162 // maxQueryChanRangeReplies specifies the default limit of replies to 163 // process for a single QueryChannelRange request. 164 maxQueryChanRangeReplies = 500 165 166 // maxQueryChanRangeRepliesZlibFactor specifies the factor applied to 167 // the maximum number of replies allowed for zlib encoded replies. 168 maxQueryChanRangeRepliesZlibFactor = 4 169 170 // chanRangeQueryBuffer is the number of blocks back that we'll go when 171 // asking the remote peer for their any channels they know of beyond 172 // our highest known channel ID. 173 chanRangeQueryBuffer = 144 174 175 // syncTransitionTimeout is the default timeout in which we'll wait up 176 // to when attempting to perform a sync transition. 177 syncTransitionTimeout = 5 * time.Second 178 179 // requestBatchSize is the maximum number of channels we will query the 180 // remote peer for in a QueryShortChanIDs message. 181 requestBatchSize = 500 182 ) 183 184 var ( 185 // encodingTypeToChunkSize maps an encoding type, to the max number of 186 // short chan ID's using the encoding type that we can fit into a 187 // single message safely. 188 encodingTypeToChunkSize = map[lnwire.ShortChanIDEncoding]int32{ 189 lnwire.EncodingSortedPlain: 8000, 190 } 191 192 // ErrGossipSyncerExiting signals that the syncer has been killed. 193 ErrGossipSyncerExiting = errors.New("gossip syncer exiting") 194 195 // ErrSyncTransitionTimeout is an error returned when we've timed out 196 // attempting to perform a sync transition. 197 ErrSyncTransitionTimeout = errors.New("timed out attempting to " + 198 "transition sync type") 199 200 // zeroTimestamp is the timestamp we'll use when we want to indicate to 201 // peers that we do not want to receive any new graph updates. 202 zeroTimestamp time.Time 203 ) 204 205 // syncTransitionReq encapsulates a request for a gossip syncer sync transition. 206 type syncTransitionReq struct { 207 newSyncType SyncerType 208 errChan chan error 209 } 210 211 // historicalSyncReq encapsulates a request for a gossip syncer to perform a 212 // historical sync. 213 type historicalSyncReq struct { 214 // doneChan is a channel that serves as a signal and is closed to ensure 215 // the historical sync is attempted by the time we return to the caller. 216 doneChan chan struct{} 217 } 218 219 // gossipSyncerCfg is a struct that packages all the information a GossipSyncer 220 // needs to carry out its duties. 221 type gossipSyncerCfg struct { 222 // chainHash is the chain that this syncer is responsible for. 223 chainHash chainhash.Hash 224 225 // peerPub is the public key of the peer we're syncing with, serialized 226 // in compressed format. 227 peerPub [33]byte 228 229 // channelSeries is the primary interface that we'll use to generate 230 // our queries and respond to the queries of the remote peer. 231 channelSeries ChannelGraphTimeSeries 232 233 // gossiperState is an interface that provides functions to persist 234 // data about the state of this gossiper. 235 gossiperState GossiperState 236 237 // encodingType is the current encoding type we're aware of. Requests 238 // with different encoding types will be rejected. 239 encodingType lnwire.ShortChanIDEncoding 240 241 // chunkSize is the max number of short chan IDs using the syncer's 242 // encoding type that we can fit into a single message safely. 243 chunkSize int32 244 245 // batchSize is the max number of channels the syncer will query from 246 // the remote node in a single QueryShortChanIDs request. 247 batchSize int32 248 249 // sendToPeer sends a variadic number of messages to the remote peer. 250 // This method should not block while waiting for sends to be written 251 // to the wire. 252 sendToPeer func(...lnwire.Message) error 253 254 // sendToPeerSync sends a variadic number of messages to the remote 255 // peer, blocking until all messages have been sent successfully or a 256 // write error is encountered. 257 sendToPeerSync func(...lnwire.Message) error 258 259 // maxUndelayedQueryReplies specifies how many gossip queries we will 260 // respond to immediately before starting to delay responses. 261 maxUndelayedQueryReplies int 262 263 // delayedQueryReplyInterval is the length of time we will wait before 264 // responding to gossip queries after replying to 265 // maxUndelayedQueryReplies queries. 266 delayedQueryReplyInterval time.Duration 267 268 // noSyncChannels will prevent the GossipSyncer from spawning a 269 // channelGraphSyncer, meaning we will not try to reconcile unknown 270 // channels with the remote peer. 271 noSyncChannels bool 272 273 // noReplyQueries will prevent the GossipSyncer from spawning a 274 // replyHandler, meaning we will not reply to queries from our remote 275 // peer. 276 noReplyQueries bool 277 278 // ignoreHistoricalFilters will prevent syncers from replying with 279 // historical data when the remote peer sets a gossip_timestamp_range. 280 // This prevents ranges with old start times from causing us to dump the 281 // graph on connect. 282 ignoreHistoricalFilters bool 283 284 // bestHeight returns the latest height known of the chain. 285 bestHeight func() uint32 286 287 // markGraphSynced updates the SyncManager's perception of whether we 288 // have completed at least one historical sync. 289 markGraphSynced func() 290 291 // maxQueryChanRangeReplies is the maximum number of replies we'll allow 292 // for a single QueryChannelRange request. 293 maxQueryChanRangeReplies uint32 294 } 295 296 // GossipSyncer is a struct that handles synchronizing the channel graph state 297 // with a remote peer. The GossipSyncer implements a state machine that will 298 // progressively ensure we're synchronized with the channel state of the remote 299 // node. Once both nodes have been synchronized, we'll use an update filter to 300 // filter out which messages should be sent to a remote peer based on their 301 // update horizon. If the update horizon isn't specified, then we won't send 302 // them any channel updates at all. 303 type GossipSyncer struct { 304 started sync.Once 305 stopped sync.Once 306 307 // state is the current state of the GossipSyncer. 308 // 309 // NOTE: This variable MUST be used atomically. 310 state uint32 311 312 // syncType denotes the SyncerType the gossip syncer is currently 313 // exercising. 314 // 315 // NOTE: This variable MUST be used atomically. 316 syncType uint32 317 318 // remoteUpdateHorizon is the update horizon of the remote peer. We'll 319 // use this to properly filter out any messages. 320 remoteUpdateHorizon *lnwire.GossipTimestampRange 321 322 // localUpdateHorizon is our local update horizon, we'll use this to 323 // determine if we've already sent out our update. 324 localUpdateHorizon *lnwire.GossipTimestampRange 325 326 // syncTransitions is a channel through which new sync type transition 327 // requests will be sent through. These requests should only be handled 328 // when the gossip syncer is in a chansSynced state to ensure its state 329 // machine behaves as expected. 330 syncTransitionReqs chan *syncTransitionReq 331 332 // historicalSyncReqs is a channel that serves as a signal for the 333 // gossip syncer to perform a historical sync. These can only be done 334 // once the gossip syncer is in a chansSynced state to ensure its state 335 // machine behaves as expected. 336 historicalSyncReqs chan *historicalSyncReq 337 338 // genHistoricalChanRangeQuery when true signals to the gossip syncer 339 // that it should request the remote peer for all of its known channel 340 // IDs starting from the genesis block of the chain. This can only 341 // happen if the gossip syncer receives a request to attempt a 342 // historical sync. It can be unset if the syncer ever transitions from 343 // PassiveSync to ActiveSync. 344 genHistoricalChanRangeQuery bool 345 346 // gossipMsgs is a channel that all responses to our queries from the 347 // target peer will be sent over, these will be read by the 348 // channelGraphSyncer. 349 gossipMsgs chan lnwire.Message 350 351 // queryMsgs is a channel that all queries from the remote peer will be 352 // received over, these will be read by the replyHandler. 353 queryMsgs chan lnwire.Message 354 355 // curQueryRangeMsg keeps track of the latest QueryChannelRange message 356 // we've sent to a peer to ensure we've consumed all expected replies. 357 // This field is primarily used within the waitingQueryChanReply state. 358 curQueryRangeMsg *lnwire.QueryChannelRange 359 360 // prevReplyChannelRange keeps track of the previous ReplyChannelRange 361 // message we've received from a peer to ensure they've fully replied to 362 // our query by ensuring they covered our requested block range. This 363 // field is primarily used within the waitingQueryChanReply state. 364 prevReplyChannelRange *lnwire.ReplyChannelRange 365 366 // bufferedChanRangeReplies is used in the waitingQueryChanReply to 367 // buffer all the chunked response to our query. 368 bufferedChanRangeReplies []lnwire.ShortChannelID 369 370 // numChanRangeRepliesRcvd is used to track the number of replies 371 // received as part of a QueryChannelRange. This field is primarily used 372 // within the waitingQueryChanReply state. 373 numChanRangeRepliesRcvd uint32 374 375 // newChansToQuery is used to pass the set of channels we should query 376 // for from the waitingQueryChanReply state to the queryNewChannels 377 // state. 378 newChansToQuery []lnwire.ShortChannelID 379 380 cfg gossipSyncerCfg 381 382 // rateLimiter dictates the frequency with which we will reply to gossip 383 // queries from a peer. This is used to delay responses to peers to 384 // prevent DOS vulnerabilities if they are spamming with an unreasonable 385 // number of queries. 386 rateLimiter *rate.Limiter 387 388 // syncedSignal is a channel that, if set, will be closed when the 389 // GossipSyncer reaches its terminal chansSynced state. 390 syncedSignal chan struct{} 391 392 sync.Mutex 393 394 quit chan struct{} 395 wg sync.WaitGroup 396 } 397 398 // newGossipSyncer returns a new instance of the GossipSyncer populated using 399 // the passed config. 400 func newGossipSyncer(cfg gossipSyncerCfg) *GossipSyncer { 401 // If no parameter was specified for max undelayed query replies, set it 402 // to the default of 5 queries. 403 if cfg.maxUndelayedQueryReplies <= 0 { 404 cfg.maxUndelayedQueryReplies = DefaultMaxUndelayedQueryReplies 405 } 406 407 // If no parameter was specified for delayed query reply interval, set 408 // to the default of 5 seconds. 409 if cfg.delayedQueryReplyInterval <= 0 { 410 cfg.delayedQueryReplyInterval = DefaultDelayedQueryReplyInterval 411 } 412 413 // Construct a rate limiter that will govern how frequently we reply to 414 // gossip queries from this peer. The limiter will automatically adjust 415 // during periods of quiescence, and increase the reply interval under 416 // load. 417 interval := rate.Every(cfg.delayedQueryReplyInterval) 418 rateLimiter := rate.NewLimiter( 419 interval, cfg.maxUndelayedQueryReplies, 420 ) 421 422 return &GossipSyncer{ 423 cfg: cfg, 424 rateLimiter: rateLimiter, 425 syncTransitionReqs: make(chan *syncTransitionReq), 426 historicalSyncReqs: make(chan *historicalSyncReq), 427 gossipMsgs: make(chan lnwire.Message, 100), 428 queryMsgs: make(chan lnwire.Message, 100), 429 quit: make(chan struct{}), 430 } 431 } 432 433 // Start starts the GossipSyncer and any goroutines that it needs to carry out 434 // its duties. 435 func (g *GossipSyncer) Start() { 436 g.started.Do(func() { 437 log.Debugf("Starting GossipSyncer(%x)", g.cfg.peerPub[:]) 438 439 // TODO(conner): only spawn channelGraphSyncer if remote 440 // supports gossip queries, and only spawn replyHandler if we 441 // advertise support 442 if !g.cfg.noSyncChannels { 443 g.wg.Add(1) 444 go g.channelGraphSyncer() 445 } 446 if !g.cfg.noReplyQueries { 447 g.wg.Add(1) 448 go g.replyHandler() 449 } 450 }) 451 } 452 453 // Stop signals the GossipSyncer for a graceful exit, then waits until it has 454 // exited. 455 func (g *GossipSyncer) Stop() { 456 g.stopped.Do(func() { 457 close(g.quit) 458 g.wg.Wait() 459 }) 460 } 461 462 // channelGraphSyncer is the main goroutine responsible for ensuring that we 463 // properly channel graph state with the remote peer, and also that we only 464 // send them messages which actually pass their defined update horizon. 465 func (g *GossipSyncer) channelGraphSyncer() { 466 defer g.wg.Done() 467 468 for { 469 state := g.syncState() 470 syncType := g.SyncType() 471 472 log.Debugf("GossipSyncer(%x): state=%v, type=%v", 473 g.cfg.peerPub[:], state, syncType) 474 475 switch state { 476 // When we're in this state, we're trying to synchronize our 477 // view of the network with the remote peer. We'll kick off 478 // this sync by asking them for the set of channels they 479 // understand, as we'll as responding to any other queries by 480 // them. 481 case syncingChans: 482 // If we're in this state, then we'll send the remote 483 // peer our opening QueryChannelRange message. 484 queryRangeMsg, err := g.genChanRangeQuery( 485 g.genHistoricalChanRangeQuery, 486 ) 487 if err != nil { 488 log.Errorf("Unable to gen chan range "+ 489 "query: %v", err) 490 return 491 } 492 493 err = g.cfg.sendToPeer(queryRangeMsg) 494 if err != nil { 495 log.Errorf("Unable to send chan range "+ 496 "query: %v", err) 497 return 498 } 499 500 // With the message sent successfully, we'll transition 501 // into the next state where we wait for their reply. 502 g.setSyncState(waitingQueryRangeReply) 503 504 // In this state, we've sent out our initial channel range 505 // query and are waiting for the final response from the remote 506 // peer before we perform a diff to see with channels they know 507 // of that we don't. 508 case waitingQueryRangeReply: 509 // We'll wait to either process a new message from the 510 // remote party, or exit due to the gossiper exiting, 511 // or us being signalled to do so. 512 select { 513 case msg := <-g.gossipMsgs: 514 // The remote peer is sending a response to our 515 // initial query, we'll collate this response, 516 // and see if it's the final one in the series. 517 // If so, we can then transition to querying 518 // for the new channels. 519 queryReply, ok := msg.(*lnwire.ReplyChannelRange) 520 if ok { 521 err := g.processChanRangeReply(queryReply) 522 if err != nil { 523 log.Errorf("Unable to "+ 524 "process chan range "+ 525 "query: %v", err) 526 return 527 } 528 continue 529 } 530 531 log.Warnf("Unexpected message: %T in state=%v", 532 msg, state) 533 534 case <-g.quit: 535 return 536 } 537 538 // We'll enter this state once we've discovered which channels 539 // the remote party knows of that we don't yet know of 540 // ourselves. 541 case queryNewChannels: 542 // First, we'll attempt to continue our channel 543 // synchronization by continuing to send off another 544 // query chunk. 545 done, err := g.synchronizeChanIDs() 546 if err != nil { 547 log.Errorf("Unable to sync chan IDs: %v", err) 548 } 549 550 // If this wasn't our last query, then we'll need to 551 // transition to our waiting state. 552 if !done { 553 g.setSyncState(waitingQueryChanReply) 554 continue 555 } 556 557 // If we're fully synchronized, then we can transition 558 // to our terminal state. 559 g.setSyncState(chansSynced) 560 561 // Ensure that the sync manager becomes aware that the 562 // historical sync completed so synced_to_graph is 563 // updated over rpc. 564 g.cfg.markGraphSynced() 565 566 // In this state, we've just sent off a new query for channels 567 // that we don't yet know of. We'll remain in this state until 568 // the remote party signals they've responded to our query in 569 // totality. 570 case waitingQueryChanReply: 571 // Once we've sent off our query, we'll wait for either 572 // an ending reply, or just another query from the 573 // remote peer. 574 select { 575 case msg := <-g.gossipMsgs: 576 // If this is the final reply to one of our 577 // queries, then we'll loop back into our query 578 // state to send of the remaining query chunks. 579 _, ok := msg.(*lnwire.ReplyShortChanIDsEnd) 580 if ok { 581 g.setSyncState(queryNewChannels) 582 continue 583 } 584 585 log.Warnf("Unexpected message: %T in state=%v", 586 msg, state) 587 588 case <-g.quit: 589 return 590 } 591 592 // This is our final terminal state where we'll only reply to 593 // any further queries by the remote peer. 594 case chansSynced: 595 g.Lock() 596 if g.syncedSignal != nil { 597 close(g.syncedSignal) 598 g.syncedSignal = nil 599 } 600 g.Unlock() 601 602 // If we haven't yet sent out our update horizon, and 603 // we want to receive real-time channel updates, we'll 604 // do so now. 605 if g.localUpdateHorizon == nil && 606 syncType.IsActiveSync() { 607 608 err := g.sendGossipTimestampRange( 609 g.initialGossipTimestamp(), math.MaxUint32, 610 ) 611 if err != nil { 612 log.Errorf("Unable to send update "+ 613 "horizon to %x: %v", 614 g.cfg.peerPub, err) 615 } 616 } 617 // With our horizon set, we'll simply reply to any new 618 // messages or process any state transitions and exit if 619 // needed. 620 fallthrough 621 622 // Pinned peers will begin in this state, since they will 623 // immediately receive a request to perform a historical sync. 624 // Otherwise, we fall through after ending in chansSynced to 625 // facilitate new requests. 626 case syncerIdle: 627 select { 628 case req := <-g.syncTransitionReqs: 629 req.errChan <- g.handleSyncTransition(req) 630 631 case req := <-g.historicalSyncReqs: 632 g.handleHistoricalSync(req) 633 634 case <-g.quit: 635 return 636 } 637 } 638 } 639 } 640 641 // replyHandler is an event loop whose sole purpose is to reply to the remote 642 // peers queries. Our replyHandler will respond to messages generated by their 643 // channelGraphSyncer, and vice versa. Each party's channelGraphSyncer drives 644 // the other's replyHandler, allowing the replyHandler to operate independently 645 // from the state machine maintained on the same node. 646 // 647 // NOTE: This method MUST be run as a goroutine. 648 func (g *GossipSyncer) replyHandler() { 649 defer g.wg.Done() 650 651 for { 652 select { 653 case msg := <-g.queryMsgs: 654 err := g.replyPeerQueries(msg) 655 switch { 656 case err == ErrGossipSyncerExiting: 657 return 658 659 case err == lnpeer.ErrPeerExiting: 660 return 661 662 case err != nil: 663 log.Errorf("Unable to reply to peer "+ 664 "query: %v", err) 665 } 666 667 case <-g.quit: 668 return 669 } 670 } 671 } 672 673 // sendGossipTimestampRange constructs and sets a GossipTimestampRange for the 674 // syncer and sends it to the remote peer. 675 func (g *GossipSyncer) sendGossipTimestampRange(firstTimestamp time.Time, 676 timestampRange uint32) error { 677 678 endTimestamp := firstTimestamp.Add( 679 time.Duration(timestampRange) * time.Second, 680 ) 681 682 log.Infof("GossipSyncer(%x): applying gossipFilter(start=%v, end=%v)", 683 g.cfg.peerPub[:], firstTimestamp, endTimestamp) 684 685 localUpdateHorizon := &lnwire.GossipTimestampRange{ 686 ChainHash: g.cfg.chainHash, 687 FirstTimestamp: uint32(firstTimestamp.Unix()), 688 TimestampRange: timestampRange, 689 } 690 691 if err := g.cfg.sendToPeer(localUpdateHorizon); err != nil { 692 return err 693 } 694 695 if firstTimestamp == zeroTimestamp && timestampRange == 0 { 696 g.localUpdateHorizon = nil 697 } else { 698 g.localUpdateHorizon = localUpdateHorizon 699 } 700 701 return nil 702 } 703 704 // synchronizeChanIDs is called by the channelGraphSyncer when we need to query 705 // the remote peer for its known set of channel IDs within a particular block 706 // range. This method will be called continually until the entire range has 707 // been queried for with a response received. We'll chunk our requests as 708 // required to ensure they fit into a single message. We may re-renter this 709 // state in the case that chunking is required. 710 func (g *GossipSyncer) synchronizeChanIDs() (bool, error) { 711 // If we're in this state yet there are no more new channels to query 712 // for, then we'll transition to our final synced state and return true 713 // to signal that we're fully synchronized. 714 if len(g.newChansToQuery) == 0 { 715 log.Infof("GossipSyncer(%x): no more chans to query", 716 g.cfg.peerPub[:]) 717 return true, nil 718 } 719 720 // Otherwise, we'll issue our next chunked query to receive replies 721 // for. 722 var queryChunk []lnwire.ShortChannelID 723 724 // If the number of channels to query for is less than the chunk size, 725 // then we can issue a single query. 726 if int32(len(g.newChansToQuery)) < g.cfg.batchSize { 727 queryChunk = g.newChansToQuery 728 g.newChansToQuery = nil 729 730 } else { 731 // Otherwise, we'll need to only query for the next chunk. 732 // We'll slice into our query chunk, then slide down our main 733 // pointer down by the chunk size. 734 queryChunk = g.newChansToQuery[:g.cfg.batchSize] 735 g.newChansToQuery = g.newChansToQuery[g.cfg.batchSize:] 736 } 737 738 log.Infof("GossipSyncer(%x): querying for %v new channels", 739 g.cfg.peerPub[:], len(queryChunk)) 740 741 // With our chunk obtained, we'll send over our next query, then return 742 // false indicating that we're net yet fully synced. 743 err := g.cfg.sendToPeer(&lnwire.QueryShortChanIDs{ 744 ChainHash: g.cfg.chainHash, 745 EncodingType: lnwire.EncodingSortedPlain, 746 ShortChanIDs: queryChunk, 747 }) 748 749 return false, err 750 } 751 752 // isLegacyReplyChannelRange determines where a ReplyChannelRange message is 753 // considered legacy. There was a point where lnd used to include the same query 754 // over multiple replies, rather than including the portion of the query the 755 // reply is handling. We'll use this as a way of detecting whether we are 756 // communicating with a legacy node so we can properly sync with them. 757 func isLegacyReplyChannelRange(query *lnwire.QueryChannelRange, 758 reply *lnwire.ReplyChannelRange) bool { 759 760 return (reply.ChainHash == query.ChainHash && 761 reply.FirstBlockHeight == query.FirstBlockHeight && 762 reply.NumBlocks == query.NumBlocks) 763 } 764 765 // processChanRangeReply is called each time the GossipSyncer receives a new 766 // reply to the initial range query to discover new channels that it didn't 767 // previously know of. 768 func (g *GossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) error { 769 // If we're not communicating with a legacy node, we'll apply some 770 // further constraints on their reply to ensure it satisfies our query. 771 if !isLegacyReplyChannelRange(g.curQueryRangeMsg, msg) { 772 // The first block should be within our original request. 773 if msg.FirstBlockHeight < g.curQueryRangeMsg.FirstBlockHeight { 774 return fmt.Errorf("reply includes channels for height "+ 775 "%v prior to query %v", msg.FirstBlockHeight, 776 g.curQueryRangeMsg.FirstBlockHeight) 777 } 778 779 // The last block should also be. We don't need to check the 780 // intermediate ones because they should already be in sorted 781 // order. 782 replyLastHeight := msg.LastBlockHeight() 783 queryLastHeight := g.curQueryRangeMsg.LastBlockHeight() 784 if replyLastHeight > queryLastHeight { 785 return fmt.Errorf("reply includes channels for height "+ 786 "%v after query %v", replyLastHeight, 787 queryLastHeight) 788 } 789 790 // If we've previously received a reply for this query, look at 791 // its last block to ensure the current reply properly follows 792 // it. 793 if g.prevReplyChannelRange != nil { 794 prevReply := g.prevReplyChannelRange 795 prevReplyLastHeight := prevReply.LastBlockHeight() 796 797 // The current reply can either start from the previous 798 // reply's last block, if there are still more channels 799 // for the same block, or the block after. 800 if msg.FirstBlockHeight != prevReplyLastHeight && 801 msg.FirstBlockHeight != prevReplyLastHeight+1 { 802 803 return fmt.Errorf("first block of reply %v "+ 804 "does not continue from last block of "+ 805 "previous %v", msg.FirstBlockHeight, 806 prevReplyLastHeight) 807 } 808 } 809 } 810 811 g.prevReplyChannelRange = msg 812 g.bufferedChanRangeReplies = append( 813 g.bufferedChanRangeReplies, msg.ShortChanIDs..., 814 ) 815 switch g.cfg.encodingType { 816 case lnwire.EncodingSortedPlain: 817 g.numChanRangeRepliesRcvd++ 818 case lnwire.EncodingSortedZlib: 819 g.numChanRangeRepliesRcvd += maxQueryChanRangeRepliesZlibFactor 820 default: 821 return fmt.Errorf("unhandled encoding type %v", g.cfg.encodingType) 822 } 823 824 log.Infof("GossipSyncer(%x): buffering chan range reply of size=%v", 825 g.cfg.peerPub[:], len(msg.ShortChanIDs)) 826 827 // If this isn't the last response and we can continue to receive more, 828 // then we can exit as we've already buffered the latest portion of the 829 // streaming reply. 830 maxReplies := g.cfg.maxQueryChanRangeReplies 831 switch { 832 // If we're communicating with a legacy node, we'll need to look at the 833 // complete field. 834 case isLegacyReplyChannelRange(g.curQueryRangeMsg, msg): 835 if msg.Complete == 0 && g.numChanRangeRepliesRcvd < maxReplies { 836 return nil 837 } 838 839 // Otherwise, we'll look at the reply's height range. 840 default: 841 replyLastHeight := msg.LastBlockHeight() 842 queryLastHeight := g.curQueryRangeMsg.LastBlockHeight() 843 844 // TODO(wilmer): This might require some padding if the remote 845 // node is not aware of the last height we sent them, i.e., is 846 // behind a few blocks from us. 847 if replyLastHeight < queryLastHeight && 848 g.numChanRangeRepliesRcvd < maxReplies { 849 return nil 850 } 851 } 852 853 log.Infof("GossipSyncer(%x): filtering through %v chans", 854 g.cfg.peerPub[:], len(g.bufferedChanRangeReplies)) 855 856 // Otherwise, this is the final response, so we'll now check to see 857 // which channels they know of that we don't. 858 newChans, err := g.cfg.channelSeries.FilterKnownChanIDs( 859 g.cfg.chainHash, g.bufferedChanRangeReplies, 860 ) 861 if err != nil { 862 return fmt.Errorf("unable to filter chan ids: %v", err) 863 } 864 865 // As we've received the entirety of the reply, we no longer need to 866 // hold on to the set of buffered replies or the original query that 867 // prompted the replies, so we'll let that be garbage collected now. 868 g.curQueryRangeMsg = nil 869 g.prevReplyChannelRange = nil 870 g.bufferedChanRangeReplies = nil 871 g.numChanRangeRepliesRcvd = 0 872 873 // If there aren't any channels that we don't know of, then we can 874 // switch straight to our terminal state. 875 if len(newChans) == 0 { 876 log.Infof("GossipSyncer(%x): remote peer has no new chans", 877 g.cfg.peerPub[:]) 878 879 g.setSyncState(chansSynced) 880 881 // Ensure that the sync manager becomes aware that the 882 // historical sync completed so synced_to_graph is updated over 883 // rpc. 884 g.cfg.markGraphSynced() 885 return nil 886 } 887 888 // Otherwise, we'll set the set of channels that we need to query for 889 // the next state, and also transition our state. 890 g.newChansToQuery = newChans 891 g.setSyncState(queryNewChannels) 892 893 log.Infof("GossipSyncer(%x): starting query for %v new chans", 894 g.cfg.peerPub[:], len(newChans)) 895 896 return nil 897 } 898 899 // genChanRangeQuery generates the initial message we'll send to the remote 900 // party when we're kicking off the channel graph synchronization upon 901 // connection. The historicalQuery boolean can be used to generate a query from 902 // the genesis block of the chain. 903 func (g *GossipSyncer) genChanRangeQuery( 904 historicalQuery bool) (*lnwire.QueryChannelRange, error) { 905 906 // First, we'll query our channel graph time series for its highest 907 // known channel ID. 908 newestChan, err := g.cfg.channelSeries.HighestChanID(g.cfg.chainHash) 909 if err != nil { 910 return nil, err 911 } 912 913 // Once we have the chan ID of the newest, we'll obtain the block height 914 // of the channel, then subtract our default horizon to ensure we don't 915 // miss any channels. By default, we go back 1 day from the newest 916 // channel, unless we're attempting a historical sync, where we'll 917 // actually start from the genesis block instead. 918 var startHeight uint32 919 switch { 920 case historicalQuery: 921 fallthrough 922 case newestChan.BlockHeight <= chanRangeQueryBuffer: 923 startHeight = 0 924 default: 925 startHeight = newestChan.BlockHeight - chanRangeQueryBuffer 926 } 927 928 // Determine the number of blocks to request based on our best height. 929 // We'll take into account any potential underflows and explicitly set 930 // numBlocks to its minimum value of 1 if so. 931 bestHeight := g.cfg.bestHeight() 932 numBlocks := bestHeight - startHeight 933 if int64(numBlocks) < 1 { 934 numBlocks = 1 935 } 936 937 log.Infof("GossipSyncer(%x): requesting new chans from height=%v "+ 938 "and %v blocks after", g.cfg.peerPub[:], startHeight, numBlocks) 939 940 // Finally, we'll craft the channel range query, using our starting 941 // height, then asking for all known channels to the foreseeable end of 942 // the main chain. 943 query := &lnwire.QueryChannelRange{ 944 ChainHash: g.cfg.chainHash, 945 FirstBlockHeight: startHeight, 946 NumBlocks: numBlocks, 947 } 948 g.curQueryRangeMsg = query 949 950 return query, nil 951 } 952 953 // replyPeerQueries is called in response to any query by the remote peer. 954 // We'll examine our state and send back our best response. 955 func (g *GossipSyncer) replyPeerQueries(msg lnwire.Message) error { 956 reservation := g.rateLimiter.Reserve() 957 delay := reservation.Delay() 958 959 // If we've already replied a handful of times, we will start to delay 960 // responses back to the remote peer. This can help prevent DOS attacks 961 // where the remote peer spams us endlessly. 962 if delay > 0 { 963 log.Infof("GossipSyncer(%x): rate limiting gossip replies, "+ 964 "responding in %s", g.cfg.peerPub[:], delay) 965 966 select { 967 case <-time.After(delay): 968 case <-g.quit: 969 return ErrGossipSyncerExiting 970 } 971 } 972 973 switch msg := msg.(type) { 974 975 // In this state, we'll also handle any incoming channel range queries 976 // from the remote peer as they're trying to sync their state as well. 977 case *lnwire.QueryChannelRange: 978 return g.replyChanRangeQuery(msg) 979 980 // If the remote peer skips straight to requesting new channels that 981 // they don't know of, then we'll ensure that we also handle this case. 982 case *lnwire.QueryShortChanIDs: 983 return g.replyShortChanIDs(msg) 984 985 default: 986 return fmt.Errorf("unknown message: %T", msg) 987 } 988 } 989 990 // replyChanRangeQuery will be dispatched in response to a channel range query 991 // by the remote node. We'll query the channel time series for channels that 992 // meet the channel range, then chunk our responses to the remote node. We also 993 // ensure that our final fragment carries the "complete" bit to indicate the 994 // end of our streaming response. 995 func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) error { 996 // Before responding, we'll check to ensure that the remote peer is 997 // querying for the same chain that we're on. If not, we'll send back a 998 // response with a complete value of zero to indicate we're on a 999 // different chain. 1000 if g.cfg.chainHash != query.ChainHash { 1001 log.Warnf("Remote peer requested QueryChannelRange for "+ 1002 "chain=%v, we're on chain=%v", query.ChainHash, 1003 g.cfg.chainHash) 1004 1005 return g.cfg.sendToPeerSync(&lnwire.ReplyChannelRange{ 1006 ChainHash: query.ChainHash, 1007 FirstBlockHeight: query.FirstBlockHeight, 1008 NumBlocks: query.NumBlocks, 1009 Complete: 0, 1010 EncodingType: g.cfg.encodingType, 1011 ShortChanIDs: nil, 1012 }) 1013 } 1014 1015 log.Infof("GossipSyncer(%x): filtering chan range: start_height=%v, "+ 1016 "num_blocks=%v", g.cfg.peerPub[:], query.FirstBlockHeight, 1017 query.NumBlocks) 1018 1019 // Next, we'll consult the time series to obtain the set of known 1020 // channel ID's that match their query. 1021 startBlock := query.FirstBlockHeight 1022 endBlock := query.LastBlockHeight() 1023 channelRanges, err := g.cfg.channelSeries.FilterChannelRange( 1024 query.ChainHash, startBlock, endBlock, 1025 ) 1026 if err != nil { 1027 return err 1028 } 1029 1030 // TODO(roasbeef): means can't send max uint above? 1031 // * or make internal 64 1032 1033 // We'll send our response in a streaming manner, chunk-by-chunk. We do 1034 // this as there's a transport message size limit which we'll need to 1035 // adhere to. We also need to make sure all of our replies cover the 1036 // expected range of the query. 1037 sendReplyForChunk := func(channelChunk []lnwire.ShortChannelID, 1038 firstHeight, lastHeight uint32, finalChunk bool) error { 1039 1040 // The number of blocks contained in the current chunk (the 1041 // total span) is the difference between the last channel ID and 1042 // the first in the range. We add one as even if all channels 1043 // returned are in the same block, we need to count that. 1044 numBlocks := lastHeight - firstHeight + 1 1045 complete := uint8(0) 1046 if finalChunk { 1047 complete = 1 1048 } 1049 1050 return g.cfg.sendToPeerSync(&lnwire.ReplyChannelRange{ 1051 ChainHash: query.ChainHash, 1052 NumBlocks: numBlocks, 1053 FirstBlockHeight: firstHeight, 1054 Complete: complete, 1055 EncodingType: g.cfg.encodingType, 1056 ShortChanIDs: channelChunk, 1057 }) 1058 } 1059 1060 var ( 1061 firstHeight = query.FirstBlockHeight 1062 lastHeight uint32 1063 channelChunk []lnwire.ShortChannelID 1064 ) 1065 for _, channelRange := range channelRanges { 1066 channels := channelRange.Channels 1067 numChannels := int32(len(channels)) 1068 numLeftToAdd := g.cfg.chunkSize - int32(len(channelChunk)) 1069 1070 // Include the current block in the ongoing chunk if it can fit 1071 // and move on to the next block. 1072 if numChannels <= numLeftToAdd { 1073 channelChunk = append(channelChunk, channels...) 1074 continue 1075 } 1076 1077 // Otherwise, we need to send our existing channel chunk as is 1078 // as its own reply and start a new one for the current block. 1079 // We'll mark the end of our current chunk as the height before 1080 // the current block to ensure the whole query range is replied 1081 // to. 1082 log.Infof("GossipSyncer(%x): sending range chunk of size=%v", 1083 g.cfg.peerPub[:], len(channelChunk)) 1084 lastHeight = channelRange.Height - 1 1085 err := sendReplyForChunk( 1086 channelChunk, firstHeight, lastHeight, false, 1087 ) 1088 if err != nil { 1089 return err 1090 } 1091 1092 // With the reply constructed, we'll start tallying channels for 1093 // our next one keeping in mind our chunk size. This may result 1094 // in channels for this block being left out from the reply, but 1095 // this isn't an issue since we'll randomly shuffle them and we 1096 // assume a historical gossip sync is performed at a later time. 1097 firstHeight = channelRange.Height 1098 chunkSize := numChannels 1099 exceedsChunkSize := numChannels > g.cfg.chunkSize 1100 if exceedsChunkSize { 1101 rand.Shuffle(len(channels), func(i, j int) { 1102 channels[i], channels[j] = channels[j], channels[i] 1103 }) 1104 chunkSize = g.cfg.chunkSize 1105 } 1106 channelChunk = channels[:chunkSize] 1107 1108 // Sort the chunk once again if we had to shuffle it. 1109 if exceedsChunkSize { 1110 sort.Slice(channelChunk, func(i, j int) bool { 1111 return channelChunk[i].ToUint64() < 1112 channelChunk[j].ToUint64() 1113 }) 1114 } 1115 } 1116 1117 // Send the remaining chunk as the final reply. 1118 log.Infof("GossipSyncer(%x): sending final chan range chunk, size=%v", 1119 g.cfg.peerPub[:], len(channelChunk)) 1120 return sendReplyForChunk( 1121 channelChunk, firstHeight, query.LastBlockHeight(), true, 1122 ) 1123 } 1124 1125 // replyShortChanIDs will be dispatched in response to a query by the remote 1126 // node for information concerning a set of short channel ID's. Our response 1127 // will be sent in a streaming chunked manner to ensure that we remain below 1128 // the current transport level message size. 1129 func (g *GossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error { 1130 // Before responding, we'll check to ensure that the remote peer is 1131 // querying for the same chain that we're on. If not, we'll send back a 1132 // response with a complete value of zero to indicate we're on a 1133 // different chain. 1134 if g.cfg.chainHash != query.ChainHash { 1135 log.Warnf("Remote peer requested QueryShortChanIDs for "+ 1136 "chain=%v, we're on chain=%v", query.ChainHash, 1137 g.cfg.chainHash) 1138 1139 return g.cfg.sendToPeerSync(&lnwire.ReplyShortChanIDsEnd{ 1140 ChainHash: query.ChainHash, 1141 Complete: 0, 1142 }) 1143 } 1144 1145 if len(query.ShortChanIDs) == 0 { 1146 log.Infof("GossipSyncer(%x): ignoring query for blank short chan ID's", 1147 g.cfg.peerPub[:]) 1148 return nil 1149 } 1150 1151 log.Infof("GossipSyncer(%x): fetching chan anns for %v chans", 1152 g.cfg.peerPub[:], len(query.ShortChanIDs)) 1153 1154 // Now that we know we're on the same chain, we'll query the channel 1155 // time series for the set of messages that we know of which satisfies 1156 // the requirement of being a chan ann, chan update, or a node ann 1157 // related to the set of queried channels. 1158 replyMsgs, err := g.cfg.channelSeries.FetchChanAnns( 1159 query.ChainHash, query.ShortChanIDs, 1160 ) 1161 if err != nil { 1162 return fmt.Errorf("unable to fetch chan anns for %v..., %v", 1163 query.ShortChanIDs[0].ToUint64(), err) 1164 } 1165 1166 // Reply with any messages related to those channel ID's, we'll write 1167 // each one individually and synchronously to throttle the sends and 1168 // perform buffering of responses in the syncer as opposed to the peer. 1169 for _, msg := range replyMsgs { 1170 err := g.cfg.sendToPeerSync(msg) 1171 if err != nil { 1172 return err 1173 } 1174 } 1175 1176 // Regardless of whether we had any messages to reply with, send over 1177 // the sentinel message to signal that the stream has terminated. 1178 return g.cfg.sendToPeerSync(&lnwire.ReplyShortChanIDsEnd{ 1179 ChainHash: query.ChainHash, 1180 Complete: 1, 1181 }) 1182 } 1183 1184 // ApplyGossipFilter applies a gossiper filter sent by the remote node to the 1185 // state machine. Once applied, we'll ensure that we don't forward any messages 1186 // to the peer that aren't within the time range of the filter. 1187 func (g *GossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) error { 1188 g.Lock() 1189 1190 g.remoteUpdateHorizon = filter 1191 1192 startTime := time.Unix(int64(g.remoteUpdateHorizon.FirstTimestamp), 0) 1193 endTime := startTime.Add( 1194 time.Duration(g.remoteUpdateHorizon.TimestampRange) * time.Second, 1195 ) 1196 1197 g.Unlock() 1198 1199 // If requested, don't reply with historical gossip data when the remote 1200 // peer sets their gossip timestamp range. 1201 if g.cfg.ignoreHistoricalFilters { 1202 return nil 1203 } 1204 1205 // Now that the remote peer has applied their filter, we'll query the 1206 // database for all the messages that are beyond this filter. 1207 newUpdatestoSend, err := g.cfg.channelSeries.UpdatesInHorizon( 1208 g.cfg.chainHash, startTime, endTime, 1209 ) 1210 if err != nil { 1211 return err 1212 } 1213 1214 log.Infof("GossipSyncer(%x): applying new update horizon: start=%v, "+ 1215 "end=%v, backlog_size=%v", g.cfg.peerPub[:], startTime, endTime, 1216 len(newUpdatestoSend)) 1217 1218 // If we don't have any to send, then we can return early. 1219 if len(newUpdatestoSend) == 0 { 1220 return nil 1221 } 1222 1223 // We'll conclude by launching a goroutine to send out any updates. 1224 g.wg.Add(1) 1225 go func() { 1226 defer g.wg.Done() 1227 1228 for _, msg := range newUpdatestoSend { 1229 err := g.cfg.sendToPeerSync(msg) 1230 switch { 1231 case err == ErrGossipSyncerExiting: 1232 return 1233 1234 case err == lnpeer.ErrPeerExiting: 1235 return 1236 1237 case err != nil: 1238 log.Errorf("Unable to send message for "+ 1239 "peer catch up: %v", err) 1240 } 1241 } 1242 }() 1243 1244 return nil 1245 } 1246 1247 // FilterGossipMsgs takes a set of gossip messages, and only send it to a peer 1248 // iff the message is within the bounds of their set gossip filter. If the peer 1249 // doesn't have a gossip filter set, then no messages will be forwarded. 1250 func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) { 1251 // If the peer doesn't have an update horizon set, then we won't send 1252 // it any new update messages. 1253 if g.remoteUpdateHorizon == nil { 1254 log.Tracef("GossipSyncer(%x): filtering due to no remoteUpdateHorizon", 1255 g.cfg.peerPub[:]) 1256 return 1257 } 1258 1259 // If we've been signaled to exit, or are exiting, then we'll stop 1260 // short. 1261 select { 1262 case <-g.quit: 1263 return 1264 default: 1265 } 1266 1267 // TODO(roasbeef): need to ensure that peer still online...send msg to 1268 // gossiper on peer termination to signal peer disconnect? 1269 1270 var err error 1271 1272 // Before we filter out the messages, we'll construct an index over the 1273 // set of channel announcements and channel updates. This will allow us 1274 // to quickly check if we should forward a chan ann, based on the known 1275 // channel updates for a channel. 1276 chanUpdateIndex := make(map[lnwire.ShortChannelID][]*lnwire.ChannelUpdate) 1277 for _, msg := range msgs { 1278 chanUpdate, ok := msg.msg.(*lnwire.ChannelUpdate) 1279 if !ok { 1280 continue 1281 } 1282 1283 chanUpdateIndex[chanUpdate.ShortChannelID] = append( 1284 chanUpdateIndex[chanUpdate.ShortChannelID], chanUpdate, 1285 ) 1286 } 1287 1288 // We'll construct a helper function that we'll us below to determine 1289 // if a given messages passes the gossip msg filter. 1290 g.Lock() 1291 startTime := time.Unix(int64(g.remoteUpdateHorizon.FirstTimestamp), 0) 1292 endTime := startTime.Add( 1293 time.Duration(g.remoteUpdateHorizon.TimestampRange) * time.Second, 1294 ) 1295 g.Unlock() 1296 1297 passesFilter := func(timeStamp uint32) bool { 1298 t := time.Unix(int64(timeStamp), 0) 1299 return t.Equal(startTime) || 1300 (t.After(startTime) && t.Before(endTime)) 1301 } 1302 1303 msgsToSend := make([]lnwire.Message, 0, len(msgs)) 1304 for _, msg := range msgs { 1305 // If the target peer is the peer that sent us this message, 1306 // then we'll exit early as we don't need to filter this 1307 // message. 1308 if _, ok := msg.senders[g.cfg.peerPub]; ok { 1309 continue 1310 } 1311 1312 switch msg := msg.msg.(type) { 1313 1314 // For each channel announcement message, we'll only send this 1315 // message if the channel updates for the channel are between 1316 // our time range. 1317 case *lnwire.ChannelAnnouncement: 1318 // First, we'll check if the channel updates are in 1319 // this message batch. 1320 chanUpdates, ok := chanUpdateIndex[msg.ShortChannelID] 1321 if !ok { 1322 // If not, we'll attempt to query the database 1323 // to see if we know of the updates. 1324 chanUpdates, err = g.cfg.channelSeries.FetchChanUpdates( 1325 g.cfg.chainHash, msg.ShortChannelID, 1326 ) 1327 if err != nil { 1328 log.Warnf("no channel updates found for "+ 1329 "short_chan_id=%s", 1330 msg.ShortChannelID) 1331 continue 1332 } 1333 } 1334 1335 for _, chanUpdate := range chanUpdates { 1336 if passesFilter(chanUpdate.Timestamp) { 1337 msgsToSend = append(msgsToSend, msg) 1338 break 1339 } 1340 } 1341 1342 if len(chanUpdates) == 0 { 1343 msgsToSend = append(msgsToSend, msg) 1344 } 1345 1346 // For each channel update, we'll only send if it the timestamp 1347 // is between our time range. 1348 case *lnwire.ChannelUpdate: 1349 if passesFilter(msg.Timestamp) { 1350 msgsToSend = append(msgsToSend, msg) 1351 } else { 1352 ts := time.Unix(int64(msg.Timestamp), 0) 1353 log.Tracef("GossipSyncer(%x): ChannelUpdate does "+ 1354 "not pass filter chan=%s ts=%s hz_start=%s "+ 1355 "hz_end=%s", g.cfg.peerPub[:], 1356 msg.ShortChannelID, ts.Format(time.RFC3339), 1357 startTime.Format(time.RFC3339), 1358 endTime.Format(time.RFC3339)) 1359 } 1360 1361 // Similarly, we only send node announcements if the update 1362 // timestamp ifs between our set gossip filter time range. 1363 case *lnwire.NodeAnnouncement: 1364 if passesFilter(msg.Timestamp) { 1365 msgsToSend = append(msgsToSend, msg) 1366 } 1367 } 1368 } 1369 1370 log.Tracef("GossipSyncer(%x): filtered gossip msgs: set=%v, sent=%v", 1371 g.cfg.peerPub[:], len(msgs), len(msgsToSend)) 1372 1373 if len(msgsToSend) == 0 { 1374 return 1375 } 1376 1377 g.cfg.sendToPeer(msgsToSend...) 1378 } 1379 1380 // ProcessQueryMsg is used by outside callers to pass new channel time series 1381 // queries to the internal processing goroutine. 1382 func (g *GossipSyncer) ProcessQueryMsg(msg lnwire.Message, peerQuit <-chan struct{}) error { 1383 var msgChan chan lnwire.Message 1384 switch msg.(type) { 1385 case *lnwire.QueryChannelRange, *lnwire.QueryShortChanIDs: 1386 msgChan = g.queryMsgs 1387 1388 // Reply messages should only be expected in states where we're waiting 1389 // for a reply. 1390 case *lnwire.ReplyChannelRange, *lnwire.ReplyShortChanIDsEnd: 1391 syncState := g.syncState() 1392 if syncState != waitingQueryRangeReply && 1393 syncState != waitingQueryChanReply { 1394 return fmt.Errorf("received unexpected query reply "+ 1395 "message %T", msg) 1396 } 1397 msgChan = g.gossipMsgs 1398 1399 default: 1400 msgChan = g.gossipMsgs 1401 } 1402 1403 select { 1404 case msgChan <- msg: 1405 case <-peerQuit: 1406 case <-g.quit: 1407 } 1408 1409 return nil 1410 } 1411 1412 // setSyncState sets the gossip syncer's state to the given state. 1413 func (g *GossipSyncer) setSyncState(state syncerState) { 1414 atomic.StoreUint32(&g.state, uint32(state)) 1415 } 1416 1417 // syncState returns the current syncerState of the target GossipSyncer. 1418 func (g *GossipSyncer) syncState() syncerState { 1419 return syncerState(atomic.LoadUint32(&g.state)) 1420 } 1421 1422 // ResetSyncedSignal returns a channel that will be closed in order to serve as 1423 // a signal for when the GossipSyncer has reached its chansSynced state. 1424 func (g *GossipSyncer) ResetSyncedSignal() chan struct{} { 1425 g.Lock() 1426 defer g.Unlock() 1427 1428 syncedSignal := make(chan struct{}) 1429 1430 syncState := syncerState(atomic.LoadUint32(&g.state)) 1431 if syncState == chansSynced { 1432 close(syncedSignal) 1433 return syncedSignal 1434 } 1435 1436 g.syncedSignal = syncedSignal 1437 return g.syncedSignal 1438 } 1439 1440 // ProcessSyncTransition sends a request to the gossip syncer to transition its 1441 // sync type to a new one. 1442 // 1443 // NOTE: This can only be done once the gossip syncer has reached its final 1444 // chansSynced state. 1445 func (g *GossipSyncer) ProcessSyncTransition(newSyncType SyncerType) error { 1446 errChan := make(chan error, 1) 1447 select { 1448 case g.syncTransitionReqs <- &syncTransitionReq{ 1449 newSyncType: newSyncType, 1450 errChan: errChan, 1451 }: 1452 case <-time.After(syncTransitionTimeout): 1453 return ErrSyncTransitionTimeout 1454 case <-g.quit: 1455 return ErrGossipSyncerExiting 1456 } 1457 1458 select { 1459 case err := <-errChan: 1460 return err 1461 case <-g.quit: 1462 return ErrGossipSyncerExiting 1463 } 1464 } 1465 1466 // handleSyncTransition handles a new sync type transition request. 1467 // 1468 // NOTE: The gossip syncer might have another sync state as a result of this 1469 // transition. 1470 func (g *GossipSyncer) handleSyncTransition(req *syncTransitionReq) error { 1471 // Return early from any NOP sync transitions. 1472 syncType := g.SyncType() 1473 if syncType == req.newSyncType { 1474 return nil 1475 } 1476 1477 log.Debugf("GossipSyncer(%x): transitioning from %v to %v", 1478 g.cfg.peerPub, syncType, req.newSyncType) 1479 1480 var ( 1481 firstTimestamp time.Time 1482 timestampRange uint32 1483 ) 1484 1485 switch req.newSyncType { 1486 // If an active sync has been requested, then we should resume receiving 1487 // new graph updates from the remote peer. 1488 case ActiveSync, PinnedSync: 1489 firstTimestamp = g.initialGossipTimestamp() 1490 timestampRange = math.MaxUint32 1491 1492 // If a PassiveSync transition has been requested, then we should no 1493 // longer receive any new updates from the remote peer. We can do this 1494 // by setting our update horizon to a range in the past ensuring no 1495 // graph updates match the timestamp range. 1496 case PassiveSync: 1497 firstTimestamp = zeroTimestamp 1498 timestampRange = 0 1499 1500 default: 1501 return fmt.Errorf("unhandled sync transition %v", 1502 req.newSyncType) 1503 } 1504 1505 err := g.sendGossipTimestampRange(firstTimestamp, timestampRange) 1506 if err != nil { 1507 return fmt.Errorf("unable to send local update horizon: %v", err) 1508 } 1509 1510 g.setSyncType(req.newSyncType) 1511 1512 return nil 1513 } 1514 1515 // setSyncType sets the gossip syncer's sync type to the given type. 1516 func (g *GossipSyncer) setSyncType(syncType SyncerType) { 1517 atomic.StoreUint32(&g.syncType, uint32(syncType)) 1518 } 1519 1520 // SyncType returns the current SyncerType of the target GossipSyncer. 1521 func (g *GossipSyncer) SyncType() SyncerType { 1522 return SyncerType(atomic.LoadUint32(&g.syncType)) 1523 } 1524 1525 // historicalSync sends a request to the gossip syncer to perofmr a historical 1526 // sync. 1527 // 1528 // NOTE: This can only be done once the gossip syncer has reached its final 1529 // chansSynced state. 1530 func (g *GossipSyncer) historicalSync() error { 1531 done := make(chan struct{}) 1532 1533 select { 1534 case g.historicalSyncReqs <- &historicalSyncReq{ 1535 doneChan: done, 1536 }: 1537 case <-time.After(syncTransitionTimeout): 1538 return ErrSyncTransitionTimeout 1539 case <-g.quit: 1540 return ErrGossiperShuttingDown 1541 } 1542 1543 select { 1544 case <-done: 1545 return nil 1546 case <-g.quit: 1547 return ErrGossiperShuttingDown 1548 } 1549 } 1550 1551 // handleHistoricalSync handles a request to the gossip syncer to perform a 1552 // historical sync. 1553 func (g *GossipSyncer) handleHistoricalSync(req *historicalSyncReq) { 1554 // We'll go back to our initial syncingChans state in order to request 1555 // the remote peer to give us all of the channel IDs they know of 1556 // starting from the genesis block. 1557 g.genHistoricalChanRangeQuery = true 1558 g.setSyncState(syncingChans) 1559 close(req.doneChan) 1560 }