github.com/decred/dcrlnd@v0.7.6/netann/chan_status_manager.go (about) 1 package netann 2 3 import ( 4 "errors" 5 "sync" 6 "time" 7 8 "github.com/decred/dcrd/dcrec/secp256k1/v4" 9 "github.com/decred/dcrd/wire" 10 "github.com/decred/dcrlnd/channeldb" 11 "github.com/decred/dcrlnd/keychain" 12 "github.com/decred/dcrlnd/lnwallet" 13 "github.com/decred/dcrlnd/lnwire" 14 ) 15 16 var ( 17 // ErrChanStatusManagerExiting signals that a shutdown of the 18 // ChanStatusManager has already been requested. 19 ErrChanStatusManagerExiting = errors.New("chan status manager exiting") 20 21 // ErrInvalidTimeoutConstraints signals that the ChanStatusManager could 22 // not be initialized because the timeouts and sample intervals were 23 // malformed. 24 ErrInvalidTimeoutConstraints = errors.New("chan-enable-timeout + " + 25 "chan-status-sample-interval must <= chan-disable-timeout " + 26 "and all three chan configs must be positive integers") 27 28 // ErrEnableInactiveChan signals that a request to enable a channel 29 // could not be completed because the channel isn't actually active at 30 // the time of the request. 31 ErrEnableInactiveChan = errors.New("unable to enable channel which " + 32 "is not currently active") 33 34 // ErrEnableManuallyDisabledChan signals that an automatic / background 35 // request to enable a channel could not be completed because the channel 36 // was manually disabled. 37 ErrEnableManuallyDisabledChan = errors.New("unable to enable channel " + 38 "which was manually disabled") 39 ) 40 41 // ChanStatusConfig holds parameters and resources required by the 42 // ChanStatusManager to perform its duty. 43 type ChanStatusConfig struct { 44 // OurPubKey is the public key identifying this node on the network. 45 OurPubKey *secp256k1.PublicKey 46 47 // OurKeyLoc is the locator for the public key identifying this node on 48 // the network. 49 OurKeyLoc keychain.KeyLocator 50 51 // MessageSigner signs messages that validate under OurPubKey. 52 MessageSigner lnwallet.MessageSigner 53 54 // IsChannelActive checks whether the channel identified by the provided 55 // ChannelID is considered active. This should only return true if the 56 // channel has been sufficiently confirmed, the channel has received 57 // FundingLocked, and the remote peer is online. 58 IsChannelActive func(lnwire.ChannelID) bool 59 60 // ApplyChannelUpdate processes new ChannelUpdates signed by our node by 61 // updating our local routing table and broadcasting the update to our 62 // peers. 63 ApplyChannelUpdate func(*lnwire.ChannelUpdate) error 64 65 // DB stores the set of channels that are to be monitored. 66 DB DB 67 68 // Graph stores the channel info and policies for channels in DB. 69 Graph ChannelGraph 70 71 // ChanEnableTimeout is the duration a peer's connect must remain stable 72 // before attempting to reenable the channel. 73 // 74 // NOTE: This value is only used to verify that the relation between 75 // itself, ChanDisableTimeout, and ChanStatusSampleInterval is correct. 76 // The user is still responsible for ensuring that the same duration 77 // elapses before attempting to reenable a channel. 78 ChanEnableTimeout time.Duration 79 80 // ChanDisableTimeout is the duration the manager will wait after 81 // detecting that a channel has become inactive before broadcasting an 82 // update to disable the channel. 83 ChanDisableTimeout time.Duration 84 85 // ChanStatusSampleInterval is the long-polling interval used by the 86 // manager to check if the channels being monitored have become 87 // inactive. 88 ChanStatusSampleInterval time.Duration 89 } 90 91 // ChanStatusManager facilitates requests to enable or disable a channel via a 92 // network announcement that sets the disable bit on the ChannelUpdate 93 // accordingly. The manager will periodically sample to detect cases where a 94 // link has become inactive, and facilitate the process of disabling the channel 95 // passively. The ChanStatusManager state machine is designed to reduce the 96 // likelihood of spamming the network with updates for flapping peers. 97 type ChanStatusManager struct { 98 started sync.Once 99 stopped sync.Once 100 101 cfg *ChanStatusConfig 102 103 // ourPubKeyBytes is the serialized compressed pubkey of our node. 104 ourPubKeyBytes []byte 105 106 // chanStates contains the set of channels being monitored for status 107 // updates. Access to the map is serialized by the statusManager's event 108 // loop. 109 chanStates channelStates 110 111 // enableRequests pipes external requests to enable a channel into the 112 // primary event loop. 113 enableRequests chan statusRequest 114 115 // disableRequests pipes external requests to disable a channel into the 116 // primary event loop. 117 disableRequests chan statusRequest 118 119 // autoRequests pipes external requests to restore automatic channel 120 // state management into the primary event loop. 121 autoRequests chan statusRequest 122 123 // statusSampleTicker fires at the interval prescribed by 124 // ChanStatusSampleInterval to check if channels in chanStates have 125 // become inactive. 126 statusSampleTicker *time.Ticker 127 128 wg sync.WaitGroup 129 quit chan struct{} 130 } 131 132 // NewChanStatusManager initializes a new ChanStatusManager using the given 133 // configuration. An error is returned if the timeouts and sample interval fail 134 // to meet do not satisfy the equation: 135 // 136 // ChanEnableTimeout + ChanStatusSampleInterval > ChanDisableTimeout. 137 func NewChanStatusManager(cfg *ChanStatusConfig) (*ChanStatusManager, error) { 138 // Assert that the config timeouts are properly formed. We require the 139 // enable_timeout + sample_interval to be less than or equal to the 140 // disable_timeout and that all are positive values. A peer that 141 // disconnects and reconnects quickly may cause a disable update to be 142 // sent, shortly followed by a reenable. Ensuring a healthy separation 143 // helps dampen the possibility of spamming updates that toggle the 144 // disable bit for such events. 145 if cfg.ChanStatusSampleInterval <= 0 { 146 return nil, ErrInvalidTimeoutConstraints 147 } 148 if cfg.ChanEnableTimeout <= 0 { 149 return nil, ErrInvalidTimeoutConstraints 150 } 151 if cfg.ChanDisableTimeout <= 0 { 152 return nil, ErrInvalidTimeoutConstraints 153 } 154 if cfg.ChanEnableTimeout+cfg.ChanStatusSampleInterval > 155 cfg.ChanDisableTimeout { 156 return nil, ErrInvalidTimeoutConstraints 157 158 } 159 160 return &ChanStatusManager{ 161 cfg: cfg, 162 ourPubKeyBytes: cfg.OurPubKey.SerializeCompressed(), 163 chanStates: make(channelStates), 164 statusSampleTicker: time.NewTicker(cfg.ChanStatusSampleInterval), 165 enableRequests: make(chan statusRequest), 166 disableRequests: make(chan statusRequest), 167 autoRequests: make(chan statusRequest), 168 quit: make(chan struct{}), 169 }, nil 170 } 171 172 // Start safely starts the ChanStatusManager. 173 func (m *ChanStatusManager) Start() error { 174 var err error 175 m.started.Do(func() { 176 err = m.start() 177 }) 178 return err 179 } 180 181 func (m *ChanStatusManager) start() error { 182 channels, err := m.fetchChannels() 183 if err != nil { 184 return err 185 } 186 187 // Populate the initial states of all confirmed, public channels. 188 for _, c := range channels { 189 _, err := m.getOrInitChanStatus(c.FundingOutpoint) 190 switch { 191 192 // If we can't retrieve the edge info for this channel, it may 193 // have been pruned from the channel graph but not yet from our 194 // set of channels. We'll skip it as we can't determine its 195 // initial state. 196 case err == channeldb.ErrEdgeNotFound: 197 log.Warnf("Unable to find channel policies for %v, "+ 198 "skipping. This is typical if the channel is "+ 199 "in the process of closing.", c.FundingOutpoint) 200 continue 201 202 // If we are in the process of opening a channel, the funding 203 // manager might not have added the ChannelUpdate to the graph 204 // yet. We'll ignore the channel for now. 205 case err == ErrUnableToExtractChanUpdate: 206 log.Warnf("Unable to find channel policies for %v, "+ 207 "skipping. This is typical if the channel is "+ 208 "in the process of being opened.", 209 c.FundingOutpoint) 210 continue 211 212 case err != nil: 213 return err 214 } 215 } 216 217 m.wg.Add(1) 218 go m.statusManager() 219 220 return nil 221 } 222 223 // Stop safely shuts down the ChanStatusManager. 224 func (m *ChanStatusManager) Stop() error { 225 m.stopped.Do(func() { 226 log.Info("Channel Status Manager shutting down") 227 close(m.quit) 228 m.wg.Wait() 229 }) 230 return nil 231 } 232 233 // RequestEnable submits a request to immediately enable a channel identified by 234 // the provided outpoint. If the channel is already enabled, no action will be 235 // taken. If the channel is marked pending-disable the channel will be returned 236 // to an active status as the scheduled disable was never sent. Otherwise if the 237 // channel is found to be disabled, a new announcement will be signed with the 238 // disabled bit cleared and broadcast to the network. 239 // 240 // If the channel was manually disabled and RequestEnable is called with 241 // manual = false, then the request will be ignored. 242 // 243 // NOTE: RequestEnable should only be called after a stable connection with the 244 // channel's peer has lasted at least the ChanEnableTimeout. Failure to do so 245 // may result in behavior that deviates from the expected behavior of the state 246 // machine. 247 func (m *ChanStatusManager) RequestEnable(outpoint wire.OutPoint, 248 manual bool) error { 249 250 return m.submitRequest(m.enableRequests, outpoint, manual) 251 } 252 253 // RequestDisable submits a request to immediately disable a channel identified 254 // by the provided outpoint. If the channel is already disabled, no action will 255 // be taken. Otherwise, a new announcement will be signed with the disabled bit 256 // set and broadcast to the network. 257 // 258 // The channel state will be changed to either ChanStatusDisabled or 259 // ChanStatusManuallyDisabled, depending on the passed-in value of manual. In 260 // particular, note the following state transitions: 261 // 262 // current state | manual | new state 263 // --------------------------------------------------- 264 // Disabled | false | Disabled 265 // ManuallyDisabled | false | ManuallyDisabled (*) 266 // Disabled | true | ManuallyDisabled 267 // ManuallyDisabled | true | ManuallyDisabled 268 // 269 // (*) If a channel was manually disabled, subsequent automatic / background 270 // 271 // requests to disable the channel do not change the fact that the channel 272 // was manually disabled. 273 func (m *ChanStatusManager) RequestDisable(outpoint wire.OutPoint, 274 manual bool) error { 275 276 return m.submitRequest(m.disableRequests, outpoint, manual) 277 } 278 279 // RequestAuto submits a request to restore automatic channel state management. 280 // If the channel is in the state ChanStatusManuallyDisabled, it will be moved 281 // back to the state ChanStatusDisabled. Otherwise, no action will be taken. 282 func (m *ChanStatusManager) RequestAuto(outpoint wire.OutPoint) error { 283 return m.submitRequest(m.autoRequests, outpoint, true) 284 } 285 286 // statusRequest is passed to the statusManager to request a change in status 287 // for a particular channel point. The exact action is governed by passing the 288 // request through one of the enableRequests or disableRequests channels. 289 type statusRequest struct { 290 outpoint wire.OutPoint 291 manual bool 292 errChan chan error 293 } 294 295 // submitRequest sends a request for either enabling or disabling a particular 296 // outpoint and awaits an error response. The request type is dictated by the 297 // reqChan passed in, which can be either of the enableRequests or 298 // disableRequests channels. 299 func (m *ChanStatusManager) submitRequest(reqChan chan statusRequest, 300 outpoint wire.OutPoint, manual bool) error { 301 302 req := statusRequest{ 303 outpoint: outpoint, 304 manual: manual, 305 errChan: make(chan error, 1), 306 } 307 308 select { 309 case reqChan <- req: 310 case <-m.quit: 311 return ErrChanStatusManagerExiting 312 } 313 314 select { 315 case err := <-req.errChan: 316 return err 317 case <-m.quit: 318 return ErrChanStatusManagerExiting 319 } 320 } 321 322 // statusManager is the primary event loop for the ChanStatusManager, providing 323 // the necessary synchronization primitive to protect access to the chanStates 324 // map. All requests to explicitly enable or disable a channel are processed 325 // within this method. The statusManager will also periodically poll the active 326 // status of channels within the htlcswitch to see if a disable announcement 327 // should be scheduled or broadcast. 328 // 329 // NOTE: This method MUST be run as a goroutine. 330 func (m *ChanStatusManager) statusManager() { 331 defer m.wg.Done() 332 333 for { 334 select { 335 336 // Process any requests to mark channel as enabled. 337 case req := <-m.enableRequests: 338 req.errChan <- m.processEnableRequest(req.outpoint, req.manual) 339 340 // Process any requests to mark channel as disabled. 341 case req := <-m.disableRequests: 342 req.errChan <- m.processDisableRequest(req.outpoint, req.manual) 343 344 // Process any requests to restore automatic channel state management. 345 case req := <-m.autoRequests: 346 req.errChan <- m.processAutoRequest(req.outpoint) 347 348 // Use long-polling to detect when channels become inactive. 349 case <-m.statusSampleTicker.C: 350 // First, do a sweep and mark any ChanStatusEnabled 351 // channels that are not active within the htlcswitch as 352 // ChanStatusPendingDisabled. The channel will then be 353 // disabled if no request to enable is received before 354 // the ChanDisableTimeout expires. 355 m.markPendingInactiveChannels() 356 357 // Now, do another sweep to disable any channels that 358 // were marked in a prior iteration as pending inactive 359 // if the inactive chan timeout has elapsed. 360 m.disableInactiveChannels() 361 362 case <-m.quit: 363 return 364 } 365 } 366 } 367 368 // processEnableRequest attempts to enable the given outpoint. 369 // 370 // - If the channel is not active at the time of the request, 371 // ErrEnableInactiveChan will be returned. 372 // - If the channel was in the ManuallyDisabled state and manual = false, 373 // the request will be ignored and ErrEnableManuallyDisabledChan will be 374 // returned. 375 // - Otherwise, the status of the channel in chanStates will be 376 // ChanStatusEnabled and the method will return nil. 377 // 378 // An update will be broadcast only if the channel is currently disabled, 379 // otherwise no update will be sent on the network. 380 func (m *ChanStatusManager) processEnableRequest(outpoint wire.OutPoint, 381 manual bool) error { 382 383 curState, err := m.getOrInitChanStatus(outpoint) 384 if err != nil { 385 return err 386 } 387 388 // Quickly check to see if the requested channel is active within the 389 // htlcswitch and return an error if it isn't. 390 chanID := lnwire.NewChanIDFromOutPoint(&outpoint) 391 if !m.cfg.IsChannelActive(chanID) { 392 return ErrEnableInactiveChan 393 } 394 395 switch curState.Status { 396 397 // Channel is already enabled, nothing to do. 398 case ChanStatusEnabled: 399 return nil 400 401 // The channel is enabled, though we are now canceling the scheduled 402 // disable. 403 case ChanStatusPendingDisabled: 404 log.Debugf("Channel(%v) already enabled, canceling scheduled "+ 405 "disable", outpoint) 406 407 // We'll sign a new update if the channel is still disabled. 408 case ChanStatusManuallyDisabled: 409 if !manual { 410 return ErrEnableManuallyDisabledChan 411 } 412 fallthrough 413 414 case ChanStatusDisabled: 415 log.Infof("Announcing channel(%v) enabled", outpoint) 416 417 err := m.signAndSendNextUpdate(outpoint, false) 418 if err != nil { 419 return err 420 } 421 } 422 423 m.chanStates.markEnabled(outpoint) 424 425 return nil 426 } 427 428 // processDisableRequest attempts to disable the given outpoint. If the method 429 // returns nil, the status of the channel in chanStates will be either 430 // ChanStatusDisabled or ChanStatusManuallyDisabled, depending on the 431 // passed-in value of manual. 432 // 433 // An update will only be sent if the channel has a status other than 434 // ChanStatusEnabled, otherwise no update will be sent on the network. 435 func (m *ChanStatusManager) processDisableRequest(outpoint wire.OutPoint, 436 manual bool) error { 437 438 curState, err := m.getOrInitChanStatus(outpoint) 439 if err != nil { 440 return err 441 } 442 443 status := curState.Status 444 if status == ChanStatusEnabled || status == ChanStatusPendingDisabled { 445 log.Infof("Announcing channel(%v) disabled [requested]", 446 outpoint) 447 448 err := m.signAndSendNextUpdate(outpoint, true) 449 if err != nil { 450 return err 451 } 452 } 453 454 // Typically, a request to disable a channel via the manager's public 455 // interface signals that the channel is being closed. 456 // 457 // If we don't need to keep track of a manual request to disable the 458 // channel, then we can remove the outpoint to free up space in the map 459 // of channel states. If for some reason the channel isn't closed, the 460 // state will be repopulated on subsequent calls to the manager's public 461 // interface via a db lookup, or on startup. 462 if manual { 463 m.chanStates.markManuallyDisabled(outpoint) 464 } else if status != ChanStatusManuallyDisabled { 465 delete(m.chanStates, outpoint) 466 } 467 468 return nil 469 } 470 471 // processAutoRequest attempts to restore automatic channel state management 472 // for the given outpoint. If the method returns nil, the state of the channel 473 // will no longer be ChanStatusManuallyDisabled (currently the only state in 474 // which automatic / background requests are ignored). 475 // 476 // No update will be sent on the network. 477 func (m *ChanStatusManager) processAutoRequest(outpoint wire.OutPoint) error { 478 curState, err := m.getOrInitChanStatus(outpoint) 479 if err != nil { 480 return err 481 } 482 483 if curState.Status == ChanStatusManuallyDisabled { 484 log.Debugf("Restoring automatic control for manually disabled "+ 485 "channel(%v)", outpoint) 486 487 m.chanStates.markDisabled(outpoint) 488 } 489 return nil 490 } 491 492 // markPendingInactiveChannels performs a sweep of the database's active 493 // channels and determines which, if any, should have a disable announcement 494 // scheduled. Once an active channel is determined to be pending-inactive, one 495 // of two transitions can follow. Either the channel is disabled because no 496 // request to enable is received before the scheduled disable is broadcast, or 497 // the channel is successfully reenabled and channel is returned to an active 498 // state from the POV of the ChanStatusManager. 499 func (m *ChanStatusManager) markPendingInactiveChannels() { 500 channels, err := m.fetchChannels() 501 if err != nil { 502 log.Errorf("Unable to load active channels: %v", err) 503 return 504 } 505 506 for _, c := range channels { 507 // Determine the initial status of the active channel, and 508 // populate the entry in the chanStates map. 509 curState, err := m.getOrInitChanStatus(c.FundingOutpoint) 510 if err != nil { 511 log.Errorf("Unable to retrieve chan status for "+ 512 "Channel(%v): %v", c.FundingOutpoint, err) 513 continue 514 } 515 516 // If the channel's status is not ChanStatusEnabled, we are 517 // done. Either it is already disabled, or it has been marked 518 // ChanStatusPendingDisable meaning that we have already 519 // scheduled the time at which it will be disabled. 520 if curState.Status != ChanStatusEnabled { 521 continue 522 } 523 524 // If our bookkeeping shows the channel as active, sample the 525 // htlcswitch to see if it believes the link is also active. If 526 // so, we will skip marking it as ChanStatusPendingDisabled. 527 chanID := lnwire.NewChanIDFromOutPoint(&c.FundingOutpoint) 528 if m.cfg.IsChannelActive(chanID) { 529 continue 530 } 531 532 // Otherwise, we discovered that this link was inactive within 533 // the switch. Compute the time at which we will send out a 534 // disable if the peer is unable to reestablish a stable 535 // connection. 536 disableTime := time.Now().Add(m.cfg.ChanDisableTimeout) 537 538 log.Debugf("Marking channel(%v) pending-inactive", 539 c.FundingOutpoint) 540 541 m.chanStates.markPendingDisabled(c.FundingOutpoint, disableTime) 542 } 543 } 544 545 // disableInactiveChannels scans through the set of monitored channels, and 546 // broadcast a disable update for any pending inactive channels whose 547 // SendDisableTime has been superseded by the current time. 548 func (m *ChanStatusManager) disableInactiveChannels() { 549 // Now, disable any channels whose inactive chan timeout has elapsed. 550 now := time.Now() 551 for outpoint, state := range m.chanStates { 552 // Ignore statuses that are not in the pending-inactive state. 553 if state.Status != ChanStatusPendingDisabled { 554 continue 555 } 556 557 // Ignore statuses for which the disable timeout has not 558 // expired. 559 if state.SendDisableTime.After(now) { 560 continue 561 } 562 563 log.Infof("Announcing channel(%v) disabled "+ 564 "[detected]", outpoint) 565 566 // Sign an update disabling the channel. 567 err := m.signAndSendNextUpdate(outpoint, true) 568 if err != nil { 569 log.Errorf("Unable to sign update disabling "+ 570 "channel(%v): %v", outpoint, err) 571 572 // If the edge was not found, this is a likely indicator 573 // that the channel has been closed. Thus we remove the 574 // outpoint from the set of tracked outpoints to prevent 575 // further attempts. 576 if err == channeldb.ErrEdgeNotFound { 577 log.Debugf("Removing channel(%v) from "+ 578 "consideration for passive disabling", 579 outpoint) 580 delete(m.chanStates, outpoint) 581 } 582 583 continue 584 } 585 586 // Record that the channel has now been disabled. 587 m.chanStates.markDisabled(outpoint) 588 } 589 } 590 591 // fetchChannels returns the working set of channels managed by the 592 // ChanStatusManager. The returned channels are filtered to only contain public 593 // channels. 594 func (m *ChanStatusManager) fetchChannels() ([]*channeldb.OpenChannel, error) { 595 allChannels, err := m.cfg.DB.FetchAllOpenChannels() 596 if err != nil { 597 return nil, err 598 } 599 600 // Filter out private channels. 601 var channels []*channeldb.OpenChannel 602 for _, c := range allChannels { 603 // We'll skip any private channels, as they aren't used for 604 // routing within the network by other nodes. 605 if c.ChannelFlags&lnwire.FFAnnounceChannel == 0 { 606 continue 607 } 608 609 channels = append(channels, c) 610 } 611 612 return channels, nil 613 } 614 615 // signAndSendNextUpdate computes and signs a valid update for the passed 616 // outpoint, with the ability to toggle the disabled bit. The new update will 617 // use the current time as the update's timestamp, or increment the old 618 // timestamp by 1 to ensure the update can propagate. If signing is successful, 619 // the new update will be sent out on the network. 620 func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint, 621 disabled bool) error { 622 623 // Retrieve the latest update for this channel. We'll use this 624 // as our starting point to send the new update. 625 chanUpdate, err := m.fetchLastChanUpdateByOutPoint(outpoint) 626 if err != nil { 627 return err 628 } 629 630 err = SignChannelUpdate( 631 m.cfg.MessageSigner, m.cfg.OurKeyLoc, chanUpdate, 632 ChanUpdSetDisable(disabled), ChanUpdSetTimestamp, 633 ) 634 if err != nil { 635 return err 636 } 637 638 return m.cfg.ApplyChannelUpdate(chanUpdate) 639 } 640 641 // fetchLastChanUpdateByOutPoint fetches the latest policy for our direction of 642 // a channel, and crafts a new ChannelUpdate with this policy. Returns an error 643 // in case our ChannelEdgePolicy is not found in the database. 644 func (m *ChanStatusManager) fetchLastChanUpdateByOutPoint(op wire.OutPoint) ( 645 *lnwire.ChannelUpdate, error) { 646 647 // Get the edge info and policies for this channel from the graph. 648 info, edge1, edge2, err := m.cfg.Graph.FetchChannelEdgesByOutpoint(&op) 649 if err != nil { 650 return nil, err 651 } 652 653 return ExtractChannelUpdate(m.ourPubKeyBytes, info, edge1, edge2) 654 } 655 656 // loadInitialChanState determines the initial ChannelState for a particular 657 // outpoint. The initial ChanStatus for a given outpoint will either be 658 // ChanStatusEnabled or ChanStatusDisabled, determined by inspecting the bits on 659 // the most recent announcement. An error is returned if the latest update could 660 // not be retrieved. 661 func (m *ChanStatusManager) loadInitialChanState( 662 outpoint *wire.OutPoint) (ChannelState, error) { 663 664 lastUpdate, err := m.fetchLastChanUpdateByOutPoint(*outpoint) 665 if err != nil { 666 return ChannelState{}, err 667 } 668 669 // Determine the channel's starting status by inspecting the disable bit 670 // on last announcement we sent out. 671 var initialStatus ChanStatus 672 if lastUpdate.ChannelFlags&lnwire.ChanUpdateDisabled == 0 { 673 initialStatus = ChanStatusEnabled 674 } else { 675 initialStatus = ChanStatusDisabled 676 } 677 678 return ChannelState{ 679 Status: initialStatus, 680 }, nil 681 } 682 683 // getOrInitChanStatus retrieves the current ChannelState for a particular 684 // outpoint. If the chanStates map already contains an entry for the outpoint, 685 // the value in the map is returned. Otherwise, the outpoint's initial status is 686 // computed and updated in the chanStates map before being returned. 687 func (m *ChanStatusManager) getOrInitChanStatus( 688 outpoint wire.OutPoint) (ChannelState, error) { 689 690 // Return the current ChannelState from the chanStates map if it is 691 // already known to the ChanStatusManager. 692 if curState, ok := m.chanStates[outpoint]; ok { 693 return curState, nil 694 } 695 696 // Otherwise, determine the initial state based on the last update we 697 // sent for the outpoint. 698 initialState, err := m.loadInitialChanState(&outpoint) 699 if err != nil { 700 return ChannelState{}, err 701 } 702 703 // Finally, store the initial state in the chanStates map. This will 704 // serve as are up-to-date view of the outpoint's current status, in 705 // addition to making the channel eligible for detecting inactivity. 706 m.chanStates[outpoint] = initialState 707 708 return initialState, nil 709 }