github.com/decred/dcrlnd@v0.7.6/channel_notifier.go (about) 1 package dcrlnd 2 3 import ( 4 "fmt" 5 "net" 6 7 "github.com/decred/dcrd/dcrec/secp256k1/v4" 8 "github.com/decred/dcrd/wire" 9 "github.com/decred/dcrlnd/chanbackup" 10 "github.com/decred/dcrlnd/channeldb" 11 "github.com/decred/dcrlnd/channelnotifier" 12 ) 13 14 // addrSource is an interface that allow us to get the addresses for a target 15 // node. We'll need this in order to be able to properly proxy the 16 // notifications to create SCBs. 17 type addrSource interface { 18 // AddrsForNode returns all known addresses for the target node public 19 // key. 20 AddrsForNode(nodePub *secp256k1.PublicKey) ([]net.Addr, error) 21 } 22 23 // channelNotifier is an implementation of the chanbackup.ChannelNotifier 24 // interface using the existing channelnotifier.ChannelNotifier struct. This 25 // implementation allows us to satisfy all the dependencies of the 26 // chanbackup.SubSwapper struct. 27 type channelNotifier struct { 28 // chanNotifier is the based channel notifier that we'll proxy requests 29 // from. 30 chanNotifier *channelnotifier.ChannelNotifier 31 32 // addrs is an implementation of the addrSource interface that allows 33 // us to get the latest set of addresses for a given node. We'll need 34 // this to be able to create an SCB for new channels. 35 addrs addrSource 36 } 37 38 // SubscribeChans requests a new channel subscription relative to the initial 39 // set of known channels. We use the knownChans as a synchronization point to 40 // ensure that the chanbackup.SubSwapper does not miss any channel open or 41 // close events in the period between when it's created, and when it requests 42 // the channel subscription. 43 // 44 // NOTE: This is part of the chanbackup.ChannelNotifier interface. 45 func (c *channelNotifier) SubscribeChans(startingChans map[wire.OutPoint]struct{}) ( 46 *chanbackup.ChannelSubscription, error) { 47 48 ltndLog.Infof("Channel backup proxy channel notifier starting") 49 50 // TODO(roasbeef): read existing set of chans and diff 51 52 quit := make(chan struct{}) 53 chanUpdates := make(chan chanbackup.ChannelEvent, 1) 54 55 // sendChanOpenUpdate is a closure that sends a ChannelEvent to the 56 // chanUpdates channel to inform subscribers about new pending or 57 // confirmed channels. 58 sendChanOpenUpdate := func(newOrPendingChan *channeldb.OpenChannel) { 59 nodeAddrs, err := c.addrs.AddrsForNode( 60 newOrPendingChan.IdentityPub, 61 ) 62 if err != nil { 63 pub := newOrPendingChan.IdentityPub 64 ltndLog.Errorf("unable to fetch addrs for %x: %v", 65 pub.SerializeCompressed(), err) 66 } 67 68 chanEvent := chanbackup.ChannelEvent{ 69 NewChans: []chanbackup.ChannelWithAddrs{ 70 { 71 OpenChannel: newOrPendingChan, 72 Addrs: nodeAddrs, 73 }, 74 }, 75 } 76 77 select { 78 case chanUpdates <- chanEvent: 79 case <-quit: 80 return 81 } 82 } 83 84 // In order to adhere to the interface, we'll proxy the events from the 85 // channel notifier to the sub-swapper in a format it understands. 86 go func() { 87 // First, we'll subscribe to the primary channel notifier so we can 88 // obtain events for new opened/closed channels. 89 chanSubscription, err := c.chanNotifier.SubscribeChannelEvents() 90 if err != nil { 91 panic(fmt.Sprintf("unable to subscribe to chans: %v", 92 err)) 93 } 94 95 defer chanSubscription.Cancel() 96 97 for { 98 select { 99 100 // A new event has been sent by the chanNotifier, we'll 101 // filter out the events we actually care about and 102 // send them to the sub-swapper. 103 case e := <-chanSubscription.Updates(): 104 // TODO(roasbeef): batch dispatch ntnfs 105 106 switch event := e.(type) { 107 // A new channel has been opened and is still 108 // pending. We can still create a backup, even 109 // if the final channel ID is not yet available. 110 case channelnotifier.PendingOpenChannelEvent: 111 pendingChan := event.PendingChannel 112 sendChanOpenUpdate(pendingChan) 113 114 // A new channel has been confirmed, we'll 115 // obtain the node address, then send to the 116 // sub-swapper. 117 case channelnotifier.OpenChannelEvent: 118 sendChanOpenUpdate(event.Channel) 119 120 // An existing channel has been closed, we'll 121 // send only the chanPoint of the closed 122 // channel to the sub-swapper. 123 case channelnotifier.ClosedChannelEvent: 124 chanPoint := event.CloseSummary.ChanPoint 125 closeType := event.CloseSummary.CloseType 126 127 // Because we see the contract as closed 128 // once our local force close TX 129 // confirms, the channel arbitrator 130 // already fires on this event. But 131 // because our funds can be in limbo for 132 // up to 2 weeks worst case we don't 133 // want to remove the crucial info we 134 // need for sweeping that time locked 135 // output before we've actually done so. 136 if closeType == channeldb.LocalForceClose { 137 ltndLog.Debugf("Channel %v "+ 138 "was force closed by "+ 139 "us, not removing "+ 140 "from channel backup "+ 141 "until fully resolved", 142 chanPoint) 143 144 continue 145 } 146 147 chanEvent := chanbackup.ChannelEvent{ 148 ClosedChans: []wire.OutPoint{ 149 chanPoint, 150 }, 151 } 152 153 select { 154 case chanUpdates <- chanEvent: 155 case <-quit: 156 return 157 } 158 159 // A channel was fully resolved on chain. This 160 // should only really interest us if it was a 161 // locally force closed channel where we didn't 162 // remove the channel already when the close 163 // event was fired. 164 case channelnotifier.FullyResolvedChannelEvent: 165 chanEvent := chanbackup.ChannelEvent{ 166 ClosedChans: []wire.OutPoint{ 167 *event.ChannelPoint, 168 }, 169 } 170 171 select { 172 case chanUpdates <- chanEvent: 173 case <-quit: 174 return 175 } 176 } 177 178 // The cancel method has been called, signalling us to 179 // exit 180 case <-quit: 181 return 182 } 183 } 184 }() 185 186 return &chanbackup.ChannelSubscription{ 187 ChanUpdates: chanUpdates, 188 Cancel: func() { 189 close(quit) 190 }, 191 }, nil 192 } 193 194 // A compile-time constraint to ensure channelNotifier implements 195 // chanbackup.ChannelNotifier. 196 var _ chanbackup.ChannelNotifier = (*channelNotifier)(nil)