github.com/decred/dcrlnd@v0.7.6/routing/notifications.go (about) 1 package routing 2 3 import ( 4 "fmt" 5 "image/color" 6 "net" 7 "sync" 8 "sync/atomic" 9 10 "github.com/davecgh/go-spew/spew" 11 "github.com/decred/dcrd/dcrec/secp256k1/v4" 12 "github.com/decred/dcrd/dcrutil/v4" 13 "github.com/decred/dcrd/wire" 14 "github.com/decred/dcrlnd/channeldb" 15 "github.com/decred/dcrlnd/lnwire" 16 "github.com/go-errors/errors" 17 ) 18 19 // TopologyClient represents an intent to receive notifications from the 20 // channel router regarding changes to the topology of the channel graph. The 21 // TopologyChanges channel will be sent upon with new updates to the channel 22 // graph in real-time as they're encountered. 23 type TopologyClient struct { 24 // TopologyChanges is a receive only channel that new channel graph 25 // updates will be sent over. 26 // 27 // TODO(roasbeef): chan for each update type instead? 28 TopologyChanges <-chan *TopologyChange 29 30 // Cancel is a function closure that should be executed when the client 31 // wishes to cancel their notification intent. Doing so allows the 32 // ChannelRouter to free up resources. 33 Cancel func() 34 } 35 36 // topologyClientUpdate is a message sent to the channel router to either 37 // register a new topology client or re-register an existing client. 38 type topologyClientUpdate struct { 39 // cancel indicates if the update to the client is cancelling an 40 // existing client's notifications. If not then this update will be to 41 // register a new set of notifications. 42 cancel bool 43 44 // clientID is the unique identifier for this client. Any further 45 // updates (deleting or adding) to this notification client will be 46 // dispatched according to the target clientID. 47 clientID uint64 48 49 // ntfnChan is a *send-only* channel in which notifications should be 50 // sent over from router -> client. 51 ntfnChan chan<- *TopologyChange 52 } 53 54 // SubscribeTopology returns a new topology client which can be used by the 55 // caller to receive notifications whenever a change in the channel graph 56 // topology occurs. Changes that will be sent at notifications include: new 57 // nodes appearing, node updating their attributes, new channels, channels 58 // closing, and updates in the routing policies of a channel's directed edges. 59 func (r *ChannelRouter) SubscribeTopology() (*TopologyClient, error) { 60 // If the router is not yet started, return an error to avoid a 61 // deadlock waiting for it to handle the subscription request. 62 if atomic.LoadUint32(&r.started) == 0 { 63 return nil, fmt.Errorf("router not started") 64 } 65 66 // We'll first atomically obtain the next ID for this client from the 67 // incrementing client ID counter. 68 clientID := atomic.AddUint64(&r.ntfnClientCounter, 1) 69 70 log.Debugf("New graph topology client subscription, client %v", 71 clientID) 72 73 ntfnChan := make(chan *TopologyChange, 10) 74 75 select { 76 case r.ntfnClientUpdates <- &topologyClientUpdate{ 77 cancel: false, 78 clientID: clientID, 79 ntfnChan: ntfnChan, 80 }: 81 case <-r.quit: 82 return nil, errors.New("ChannelRouter shutting down") 83 } 84 85 return &TopologyClient{ 86 TopologyChanges: ntfnChan, 87 Cancel: func() { 88 select { 89 case r.ntfnClientUpdates <- &topologyClientUpdate{ 90 cancel: true, 91 clientID: clientID, 92 }: 93 case <-r.quit: 94 return 95 } 96 }, 97 }, nil 98 } 99 100 // topologyClient is a data-structure use by the channel router to couple the 101 // client's notification channel along with a special "exit" channel that can 102 // be used to cancel all lingering goroutines blocked on a send to the 103 // notification channel. 104 type topologyClient struct { 105 // ntfnChan is a send-only channel that's used to propagate 106 // notification s from the channel router to an instance of a 107 // topologyClient client. 108 ntfnChan chan<- *TopologyChange 109 110 // exit is a channel that is used internally by the channel router to 111 // cancel any active un-consumed goroutine notifications. 112 exit chan struct{} 113 114 wg sync.WaitGroup 115 } 116 117 // notifyTopologyChange notifies all registered clients of a new change in 118 // graph topology in a non-blocking. 119 func (r *ChannelRouter) notifyTopologyChange(topologyDiff *TopologyChange) { 120 r.RLock() 121 defer r.RUnlock() 122 123 numClients := len(r.topologyClients) 124 if numClients == 0 { 125 return 126 } 127 128 log.Tracef("Sending topology notification to %v clients %v", 129 numClients, newLogClosure(func() string { 130 return spew.Sdump(topologyDiff) 131 }), 132 ) 133 log.Debugf("Topology notification to %v clients, nodes=%d, updates=%d, closed=%d", 134 numClients, len(topologyDiff.NodeUpdates), len(topologyDiff.ChannelEdgeUpdates), 135 len(topologyDiff.ClosedChannels)) 136 137 for _, client := range r.topologyClients { 138 client.wg.Add(1) 139 140 go func(c *topologyClient) { 141 defer c.wg.Done() 142 143 select { 144 145 // In this case we'll try to send the notification 146 // directly to the upstream client consumer. 147 case c.ntfnChan <- topologyDiff: 148 149 // If the client cancels the notifications, then we'll 150 // exit early. 151 case <-c.exit: 152 153 // Similarly, if the ChannelRouter itself exists early, 154 // then we'll also exit ourselves. 155 case <-r.quit: 156 157 } 158 }(client) 159 } 160 } 161 162 // TopologyChange represents a new set of modifications to the channel graph. 163 // Topology changes will be dispatched in real-time as the ChannelGraph 164 // validates and process modifications to the authenticated channel graph. 165 type TopologyChange struct { 166 // NodeUpdates is a slice of nodes which are either new to the channel 167 // graph, or have had their attributes updated in an authenticated 168 // manner. 169 NodeUpdates []*NetworkNodeUpdate 170 171 // ChanelEdgeUpdates is a slice of channel edges which are either newly 172 // opened and authenticated, or have had their routing policies 173 // updated. 174 ChannelEdgeUpdates []*ChannelEdgeUpdate 175 176 // ClosedChannels contains a slice of close channel summaries which 177 // described which block a channel was closed at, and also carry 178 // supplemental information such as the capacity of the former channel. 179 ClosedChannels []*ClosedChanSummary 180 } 181 182 // isEmpty returns true if the TopologyChange is empty. A TopologyChange is 183 // considered empty, if it contains no *new* updates of any type. 184 func (t *TopologyChange) isEmpty() bool { 185 return len(t.NodeUpdates) == 0 && len(t.ChannelEdgeUpdates) == 0 && 186 len(t.ClosedChannels) == 0 187 } 188 189 // ClosedChanSummary is a summary of a channel that was detected as being 190 // closed by monitoring the blockchain. Once a channel's funding point has been 191 // spent, the channel will automatically be marked as closed by the 192 // ChainNotifier. 193 // 194 // TODO(roasbeef): add nodes involved? 195 type ClosedChanSummary struct { 196 // ChanID is the short-channel ID which uniquely identifies the 197 // channel. 198 ChanID uint64 199 200 // Capacity was the total capacity of the channel before it was closed. 201 Capacity dcrutil.Amount 202 203 // ClosedHeight is the height in the chain that the channel was closed 204 // at. 205 ClosedHeight uint32 206 207 // ChanPoint is the funding point, or the multi-sig utxo which 208 // previously represented the channel. 209 ChanPoint wire.OutPoint 210 } 211 212 // createCloseSummaries takes in a slice of channels closed at the target block 213 // height and creates a slice of summaries which of each channel closure. 214 func createCloseSummaries(blockHeight uint32, 215 closedChans ...*channeldb.ChannelEdgeInfo) []*ClosedChanSummary { 216 217 closeSummaries := make([]*ClosedChanSummary, len(closedChans)) 218 for i, closedChan := range closedChans { 219 closeSummaries[i] = &ClosedChanSummary{ 220 ChanID: closedChan.ChannelID, 221 Capacity: closedChan.Capacity, 222 ClosedHeight: blockHeight, 223 ChanPoint: closedChan.ChannelPoint, 224 } 225 } 226 227 return closeSummaries 228 } 229 230 // NetworkNodeUpdate is an update for a node within the Lightning Network. A 231 // NetworkNodeUpdate is sent out either when a new node joins the network, or a 232 // node broadcasts a new update with a newer time stamp that supersedes its 233 // old update. All updates are properly authenticated. 234 type NetworkNodeUpdate struct { 235 // Addresses is a slice of all the node's known addresses. 236 Addresses []net.Addr 237 238 // IdentityKey is the identity public key of the target node. This is 239 // used to encrypt onion blobs as well as to authenticate any new 240 // updates. 241 IdentityKey *secp256k1.PublicKey 242 243 // Alias is the alias or nick name of the node. 244 Alias string 245 246 // Color is the node's color in hex code format. 247 Color string 248 249 // Features holds the set of features the node supports. 250 Features *lnwire.FeatureVector 251 } 252 253 // ChannelEdgeUpdate is an update for a new channel within the ChannelGraph. 254 // This update is sent out once a new authenticated channel edge is discovered 255 // within the network. These updates are directional, so if a channel is fully 256 // public, then there will be two updates sent out: one for each direction 257 // within the channel. Each update will carry that particular routing edge 258 // policy for the channel direction. 259 // 260 // An edge is a channel in the direction of AdvertisingNode -> ConnectingNode. 261 type ChannelEdgeUpdate struct { 262 // ChanID is the unique short channel ID for the channel. This encodes 263 // where in the blockchain the channel's funding transaction was 264 // originally confirmed. 265 ChanID uint64 266 267 // ChanPoint is the outpoint which represents the multi-sig funding 268 // output for the channel. 269 ChanPoint wire.OutPoint 270 271 // Capacity is the capacity of the newly created channel. 272 Capacity dcrutil.Amount 273 274 // MinHTLC is the minimum HTLC amount that this channel will forward. 275 MinHTLC lnwire.MilliAtom 276 277 // MaxHTLC is the maximum HTLC amount that this channel will forward. 278 MaxHTLC lnwire.MilliAtom 279 280 // BaseFee is the base fee that will charged for all HTLC's forwarded 281 // across the this channel direction. 282 BaseFee lnwire.MilliAtom 283 284 // FeeRate is the fee rate that will be shared for all HTLC's forwarded 285 // across this channel direction. 286 FeeRate lnwire.MilliAtom 287 288 // TimeLockDelta is the time-lock expressed in blocks that will be 289 // added to outgoing HTLC's from incoming HTLC's. This value is the 290 // difference of the incoming and outgoing HTLC's time-locks routed 291 // through this hop. 292 TimeLockDelta uint16 293 294 // AdvertisingNode is the node that's advertising this edge. 295 AdvertisingNode *secp256k1.PublicKey 296 297 // ConnectingNode is the node that the advertising node connects to. 298 ConnectingNode *secp256k1.PublicKey 299 300 // Disabled, if true, signals that the channel is unavailable to relay 301 // payments. 302 Disabled bool 303 } 304 305 // appendTopologyChange appends the passed update message to the passed 306 // TopologyChange, properly identifying which type of update the message 307 // constitutes. This function will also fetch any required auxiliary 308 // information required to create the topology change update from the graph 309 // database. 310 func addToTopologyChange(graph *channeldb.ChannelGraph, update *TopologyChange, 311 msg interface{}) error { 312 313 switch m := msg.(type) { 314 315 // Any node announcement maps directly to a NetworkNodeUpdate struct. 316 // No further data munging or db queries are required. 317 case *channeldb.LightningNode: 318 pubKey, err := m.PubKey() 319 if err != nil { 320 return err 321 } 322 323 nodeUpdate := &NetworkNodeUpdate{ 324 Addresses: m.Addresses, 325 IdentityKey: pubKey, 326 Alias: m.Alias, 327 Color: EncodeHexColor(m.Color), 328 Features: m.Features.Clone(), 329 } 330 331 update.NodeUpdates = append(update.NodeUpdates, nodeUpdate) 332 log.Debugf("Topology change: New node %x", m.PubKeyBytes) 333 return nil 334 335 // We ignore initial channel announcements as we'll only send out 336 // updates once the individual edges themselves have been updated. 337 case *channeldb.ChannelEdgeInfo: 338 log.Debugf("ChannelEdgeInfo not a topology change") 339 return nil 340 341 // Any new ChannelUpdateAnnouncements will generate a corresponding 342 // ChannelEdgeUpdate notification. 343 case *channeldb.ChannelEdgePolicy: 344 // We'll need to fetch the edge's information from the database 345 // in order to get the information concerning which nodes are 346 // being connected. 347 edgeInfo, _, _, err := graph.FetchChannelEdgesByID(m.ChannelID) 348 if err != nil { 349 return errors.Errorf("unable fetch channel edge: %v", 350 err) 351 } 352 353 // If the flag is one, then the advertising node is actually 354 // the second node. 355 sourceNode := edgeInfo.NodeKey1 356 connectingNode := edgeInfo.NodeKey2 357 if m.ChannelFlags&lnwire.ChanUpdateDirection == 1 { 358 sourceNode = edgeInfo.NodeKey2 359 connectingNode = edgeInfo.NodeKey1 360 } 361 362 aNode, err := sourceNode() 363 if err != nil { 364 return err 365 } 366 cNode, err := connectingNode() 367 if err != nil { 368 return err 369 } 370 371 edgeUpdate := &ChannelEdgeUpdate{ 372 ChanID: m.ChannelID, 373 ChanPoint: edgeInfo.ChannelPoint, 374 TimeLockDelta: m.TimeLockDelta, 375 Capacity: edgeInfo.Capacity, 376 MinHTLC: m.MinHTLC, 377 MaxHTLC: m.MaxHTLC, 378 BaseFee: m.FeeBaseMAtoms, 379 FeeRate: m.FeeProportionalMillionths, 380 AdvertisingNode: aNode, 381 ConnectingNode: cNode, 382 Disabled: m.ChannelFlags.IsDisabled(), 383 } 384 385 // TODO(roasbeef): add bit to toggle 386 update.ChannelEdgeUpdates = append(update.ChannelEdgeUpdates, 387 edgeUpdate) 388 log.Infof("Topology change: ChannelEdge %s (%s) disabled:%v", 389 edgeInfo.ChannelPoint, lnwire.NewShortChanIDFromInt(m.ChannelID), edgeUpdate.Disabled) 390 return nil 391 392 default: 393 return fmt.Errorf("unable to add to topology change, "+ 394 "unknown message type %T", msg) 395 } 396 } 397 398 // EncodeHexColor takes a color and returns it in hex code format. 399 func EncodeHexColor(color color.RGBA) string { 400 return fmt.Sprintf("#%02x%02x%02x", color.R, color.G, color.B) 401 }