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  }