github.com/decred/dcrlnd@v0.7.6/channeldb/graph_cache.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/decred/dcrd/dcrutil/v4"
     8  
     9  	"github.com/decred/dcrlnd/kvdb"
    10  	"github.com/decred/dcrlnd/lnwire"
    11  	"github.com/decred/dcrlnd/routing/route"
    12  )
    13  
    14  // GraphCacheNode is an interface for all the information the cache needs to know
    15  // about a lightning node.
    16  type GraphCacheNode interface {
    17  	// PubKey is the node's public identity key.
    18  	PubKey() route.Vertex
    19  
    20  	// Features returns the node's p2p features.
    21  	Features() *lnwire.FeatureVector
    22  
    23  	// ForEachChannel iterates through all channels of a given node,
    24  	// executing the passed callback with an edge info structure and the
    25  	// policies of each end of the channel. The first edge policy is the
    26  	// outgoing edge *to* the connecting node, while the second is the
    27  	// incoming edge *from* the connecting node. If the callback returns an
    28  	// error, then the iteration is halted with the error propagated back up
    29  	// to the caller.
    30  	ForEachChannel(kvdb.RTx,
    31  		func(kvdb.RTx, *ChannelEdgeInfo, *ChannelEdgePolicy,
    32  			*ChannelEdgePolicy) error) error
    33  }
    34  
    35  // CachedEdgePolicy is a struct that only caches the information of a
    36  // ChannelEdgePolicy that we actually use for pathfinding and therefore need to
    37  // store in the cache.
    38  type CachedEdgePolicy struct {
    39  	// ChannelID is the unique channel ID for the channel. The first 3
    40  	// bytes are the block height, the next 3 the index within the block,
    41  	// and the last 2 bytes are the output index for the channel.
    42  	ChannelID uint64
    43  
    44  	// MessageFlags is a bitfield which indicates the presence of optional
    45  	// fields (like max_htlc) in the policy.
    46  	MessageFlags lnwire.ChanUpdateMsgFlags
    47  
    48  	// ChannelFlags is a bitfield which signals the capabilities of the
    49  	// channel as well as the directed edge this update applies to.
    50  	ChannelFlags lnwire.ChanUpdateChanFlags
    51  
    52  	// TimeLockDelta is the number of blocks this node will subtract from
    53  	// the expiry of an incoming HTLC. This value expresses the time buffer
    54  	// the node would like to HTLC exchanges.
    55  	TimeLockDelta uint16
    56  
    57  	// MinHTLC is the smallest value HTLC this node will forward, expressed
    58  	// in millisatoshi.
    59  	MinHTLC lnwire.MilliAtom
    60  
    61  	// MaxHTLC is the largest value HTLC this node will forward, expressed
    62  	// in millisatoshi.
    63  	MaxHTLC lnwire.MilliAtom
    64  
    65  	// FeeBaseMAtoms is the base HTLC fee that will be charged for forwarding
    66  	// ANY HTLC, expressed in mSAT's.
    67  	FeeBaseMAtoms lnwire.MilliAtom
    68  
    69  	// FeeProportionalMillionths is the rate that the node will charge for
    70  	// HTLCs for each millionth of a satoshi forwarded.
    71  	FeeProportionalMillionths lnwire.MilliAtom
    72  
    73  	// ToNodePubKey is a function that returns the to node of a policy.
    74  	// Since we only ever store the inbound policy, this is always the node
    75  	// that we query the channels for in ForEachChannel(). Therefore, we can
    76  	// save a lot of space by not storing this information in the memory and
    77  	// instead just set this function when we copy the policy from cache in
    78  	// ForEachChannel().
    79  	ToNodePubKey func() route.Vertex
    80  
    81  	// ToNodeFeatures are the to node's features. They are never set while
    82  	// the edge is in the cache, only on the copy that is returned in
    83  	// ForEachChannel().
    84  	ToNodeFeatures *lnwire.FeatureVector
    85  }
    86  
    87  // ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
    88  // the passed active payment channel. This value is currently computed as
    89  // specified in BOLT07, but will likely change in the near future.
    90  func (c *CachedEdgePolicy) ComputeFee(
    91  	amt lnwire.MilliAtom) lnwire.MilliAtom {
    92  
    93  	return c.FeeBaseMAtoms + (amt*c.FeeProportionalMillionths)/feeRateParts
    94  }
    95  
    96  // ComputeFeeFromIncoming computes the fee to forward an HTLC given the incoming
    97  // amount.
    98  func (c *CachedEdgePolicy) ComputeFeeFromIncoming(
    99  	incomingAmt lnwire.MilliAtom) lnwire.MilliAtom {
   100  
   101  	return incomingAmt - divideCeil(
   102  		feeRateParts*(incomingAmt-c.FeeBaseMAtoms),
   103  		feeRateParts+c.FeeProportionalMillionths,
   104  	)
   105  }
   106  
   107  // NewCachedPolicy turns a full policy into a minimal one that can be cached.
   108  func NewCachedPolicy(policy *ChannelEdgePolicy) *CachedEdgePolicy {
   109  	return &CachedEdgePolicy{
   110  		ChannelID:                 policy.ChannelID,
   111  		MessageFlags:              policy.MessageFlags,
   112  		ChannelFlags:              policy.ChannelFlags,
   113  		TimeLockDelta:             policy.TimeLockDelta,
   114  		MinHTLC:                   policy.MinHTLC,
   115  		MaxHTLC:                   policy.MaxHTLC,
   116  		FeeBaseMAtoms:             policy.FeeBaseMAtoms,
   117  		FeeProportionalMillionths: policy.FeeProportionalMillionths,
   118  	}
   119  }
   120  
   121  // DirectedChannel is a type that stores the channel information as seen from
   122  // one side of the channel.
   123  type DirectedChannel struct {
   124  	// ChannelID is the unique identifier of this channel.
   125  	ChannelID uint64
   126  
   127  	// IsNode1 indicates if this is the node with the smaller public key.
   128  	IsNode1 bool
   129  
   130  	// OtherNode is the public key of the node on the other end of this
   131  	// channel.
   132  	OtherNode route.Vertex
   133  
   134  	// Capacity is the announced capacity of this channel in satoshis.
   135  	Capacity dcrutil.Amount
   136  
   137  	// OutPolicySet is a boolean that indicates whether the node has an
   138  	// outgoing policy set. For pathfinding only the existence of the policy
   139  	// is important to know, not the actual content.
   140  	OutPolicySet bool
   141  
   142  	// InPolicy is the incoming policy *from* the other node to this node.
   143  	// In path finding, we're walking backward from the destination to the
   144  	// source, so we're always interested in the edge that arrives to us
   145  	// from the other node.
   146  	InPolicy *CachedEdgePolicy
   147  }
   148  
   149  // DeepCopy creates a deep copy of the channel, including the incoming policy.
   150  func (c *DirectedChannel) DeepCopy() *DirectedChannel {
   151  	channelCopy := *c
   152  
   153  	if channelCopy.InPolicy != nil {
   154  		inPolicyCopy := *channelCopy.InPolicy
   155  		channelCopy.InPolicy = &inPolicyCopy
   156  
   157  		// The fields for the ToNode can be overwritten by the path
   158  		// finding algorithm, which is why we need a deep copy in the
   159  		// first place. So we always start out with nil values, just to
   160  		// be sure they don't contain any old data.
   161  		channelCopy.InPolicy.ToNodePubKey = nil
   162  		channelCopy.InPolicy.ToNodeFeatures = nil
   163  	}
   164  
   165  	return &channelCopy
   166  }
   167  
   168  // GraphCache is a type that holds a minimal set of information of the public
   169  // channel graph that can be used for pathfinding.
   170  type GraphCache struct {
   171  	nodeChannels map[route.Vertex]map[uint64]*DirectedChannel
   172  	nodeFeatures map[route.Vertex]*lnwire.FeatureVector
   173  
   174  	mtx sync.RWMutex
   175  }
   176  
   177  // NewGraphCache creates a new graphCache.
   178  func NewGraphCache(preAllocNumNodes int) *GraphCache {
   179  	return &GraphCache{
   180  		nodeChannels: make(
   181  			map[route.Vertex]map[uint64]*DirectedChannel,
   182  			// A channel connects two nodes, so we can look it up
   183  			// from both sides, meaning we get double the number of
   184  			// entries.
   185  			preAllocNumNodes*2,
   186  		),
   187  		nodeFeatures: make(
   188  			map[route.Vertex]*lnwire.FeatureVector,
   189  			preAllocNumNodes,
   190  		),
   191  	}
   192  }
   193  
   194  // Stats returns statistics about the current cache size.
   195  func (c *GraphCache) Stats() string {
   196  	c.mtx.RLock()
   197  	defer c.mtx.RUnlock()
   198  
   199  	numChannels := 0
   200  	for node := range c.nodeChannels {
   201  		numChannels += len(c.nodeChannels[node])
   202  	}
   203  	return fmt.Sprintf("num_node_features=%d, num_nodes=%d, "+
   204  		"num_channels=%d", len(c.nodeFeatures), len(c.nodeChannels),
   205  		numChannels)
   206  }
   207  
   208  // AddNodeFeatures adds a graph node and its features to the cache.
   209  func (c *GraphCache) AddNodeFeatures(node GraphCacheNode) {
   210  	nodePubKey := node.PubKey()
   211  
   212  	// Only hold the lock for a short time. The `ForEachChannel()` below is
   213  	// possibly slow as it has to go to the backend, so we can unlock
   214  	// between the calls. And the AddChannel() method will acquire its own
   215  	// lock anyway.
   216  	c.mtx.Lock()
   217  	c.nodeFeatures[nodePubKey] = node.Features()
   218  	c.mtx.Unlock()
   219  }
   220  
   221  // AddNode adds a graph node, including all the (directed) channels of that
   222  // node.
   223  func (c *GraphCache) AddNode(tx kvdb.RTx, node GraphCacheNode) error {
   224  	c.AddNodeFeatures(node)
   225  
   226  	return node.ForEachChannel(
   227  		tx, func(tx kvdb.RTx, info *ChannelEdgeInfo,
   228  			outPolicy *ChannelEdgePolicy,
   229  			inPolicy *ChannelEdgePolicy) error {
   230  
   231  			c.AddChannel(info, outPolicy, inPolicy)
   232  
   233  			return nil
   234  		},
   235  	)
   236  }
   237  
   238  // AddChannel adds a non-directed channel, meaning that the order of policy 1
   239  // and policy 2 does not matter, the directionality is extracted from the info
   240  // and policy flags automatically. The policy will be set as the outgoing policy
   241  // on one node and the incoming policy on the peer's side.
   242  func (c *GraphCache) AddChannel(info *ChannelEdgeInfo,
   243  	policy1 *ChannelEdgePolicy, policy2 *ChannelEdgePolicy) {
   244  
   245  	if info == nil {
   246  		return
   247  	}
   248  
   249  	if policy1 != nil && policy1.IsDisabled() &&
   250  		policy2 != nil && policy2.IsDisabled() {
   251  
   252  		return
   253  	}
   254  
   255  	// Create the edge entry for both nodes.
   256  	c.mtx.Lock()
   257  	c.updateOrAddEdge(info.NodeKey1Bytes, &DirectedChannel{
   258  		ChannelID: info.ChannelID,
   259  		IsNode1:   true,
   260  		OtherNode: info.NodeKey2Bytes,
   261  		Capacity:  info.Capacity,
   262  	})
   263  	c.updateOrAddEdge(info.NodeKey2Bytes, &DirectedChannel{
   264  		ChannelID: info.ChannelID,
   265  		IsNode1:   false,
   266  		OtherNode: info.NodeKey1Bytes,
   267  		Capacity:  info.Capacity,
   268  	})
   269  	c.mtx.Unlock()
   270  
   271  	// The policy's node is always the to_node. So if policy 1 has to_node
   272  	// of node 2 then we have the policy 1 as seen from node 1.
   273  	if policy1 != nil {
   274  		fromNode, toNode := info.NodeKey1Bytes, info.NodeKey2Bytes
   275  		if policy1.Node.PubKeyBytes != info.NodeKey2Bytes {
   276  			fromNode, toNode = toNode, fromNode
   277  		}
   278  		isEdge1 := policy1.ChannelFlags&lnwire.ChanUpdateDirection == 0
   279  		c.UpdatePolicy(policy1, fromNode, toNode, isEdge1)
   280  	}
   281  	if policy2 != nil {
   282  		fromNode, toNode := info.NodeKey2Bytes, info.NodeKey1Bytes
   283  		if policy2.Node.PubKeyBytes != info.NodeKey1Bytes {
   284  			fromNode, toNode = toNode, fromNode
   285  		}
   286  		isEdge1 := policy2.ChannelFlags&lnwire.ChanUpdateDirection == 0
   287  		c.UpdatePolicy(policy2, fromNode, toNode, isEdge1)
   288  	}
   289  }
   290  
   291  // updateOrAddEdge makes sure the edge information for a node is either updated
   292  // if it already exists or is added to that node's list of channels.
   293  func (c *GraphCache) updateOrAddEdge(node route.Vertex, edge *DirectedChannel) {
   294  	if len(c.nodeChannels[node]) == 0 {
   295  		c.nodeChannels[node] = make(map[uint64]*DirectedChannel)
   296  	}
   297  
   298  	c.nodeChannels[node][edge.ChannelID] = edge
   299  }
   300  
   301  // UpdatePolicy updates a single policy on both the from and to node. The order
   302  // of the from and to node is not strictly important. But we assume that a
   303  // channel edge was added beforehand so that the directed channel struct already
   304  // exists in the cache.
   305  func (c *GraphCache) UpdatePolicy(policy *ChannelEdgePolicy, fromNode,
   306  	toNode route.Vertex, edge1 bool) {
   307  
   308  	c.mtx.Lock()
   309  	defer c.mtx.Unlock()
   310  
   311  	updatePolicy := func(nodeKey route.Vertex) {
   312  		if len(c.nodeChannels[nodeKey]) == 0 {
   313  			return
   314  		}
   315  
   316  		channel, ok := c.nodeChannels[nodeKey][policy.ChannelID]
   317  		if !ok {
   318  			return
   319  		}
   320  
   321  		// Edge 1 is defined as the policy for the direction of node1 to
   322  		// node2.
   323  		switch {
   324  		// This is node 1, and it is edge 1, so this is the outgoing
   325  		// policy for node 1.
   326  		case channel.IsNode1 && edge1:
   327  			channel.OutPolicySet = true
   328  
   329  		// This is node 2, and it is edge 2, so this is the outgoing
   330  		// policy for node 2.
   331  		case !channel.IsNode1 && !edge1:
   332  			channel.OutPolicySet = true
   333  
   334  		// The other two cases left mean it's the inbound policy for the
   335  		// node.
   336  		default:
   337  			channel.InPolicy = NewCachedPolicy(policy)
   338  		}
   339  	}
   340  
   341  	updatePolicy(fromNode)
   342  	updatePolicy(toNode)
   343  }
   344  
   345  // RemoveNode completely removes a node and all its channels (including the
   346  // peer's side).
   347  func (c *GraphCache) RemoveNode(node route.Vertex) {
   348  	c.mtx.Lock()
   349  	defer c.mtx.Unlock()
   350  
   351  	delete(c.nodeFeatures, node)
   352  
   353  	// First remove all channels from the other nodes' lists.
   354  	for _, channel := range c.nodeChannels[node] {
   355  		c.removeChannelIfFound(channel.OtherNode, channel.ChannelID)
   356  	}
   357  
   358  	// Then remove our whole node completely.
   359  	delete(c.nodeChannels, node)
   360  }
   361  
   362  // RemoveChannel removes a single channel between two nodes.
   363  func (c *GraphCache) RemoveChannel(node1, node2 route.Vertex, chanID uint64) {
   364  	c.mtx.Lock()
   365  	defer c.mtx.Unlock()
   366  
   367  	// Remove that one channel from both sides.
   368  	c.removeChannelIfFound(node1, chanID)
   369  	c.removeChannelIfFound(node2, chanID)
   370  }
   371  
   372  // removeChannelIfFound removes a single channel from one side.
   373  func (c *GraphCache) removeChannelIfFound(node route.Vertex, chanID uint64) {
   374  	if len(c.nodeChannels[node]) == 0 {
   375  		return
   376  	}
   377  
   378  	delete(c.nodeChannels[node], chanID)
   379  }
   380  
   381  // UpdateChannel updates the channel edge information for a specific edge. We
   382  // expect the edge to already exist and be known. If it does not yet exist, this
   383  // call is a no-op.
   384  func (c *GraphCache) UpdateChannel(info *ChannelEdgeInfo) {
   385  	c.mtx.Lock()
   386  	defer c.mtx.Unlock()
   387  
   388  	if len(c.nodeChannels[info.NodeKey1Bytes]) == 0 ||
   389  		len(c.nodeChannels[info.NodeKey2Bytes]) == 0 {
   390  
   391  		return
   392  	}
   393  
   394  	channel, ok := c.nodeChannels[info.NodeKey1Bytes][info.ChannelID]
   395  	if ok {
   396  		// We only expect to be called when the channel is already
   397  		// known.
   398  		channel.Capacity = info.Capacity
   399  		channel.OtherNode = info.NodeKey2Bytes
   400  	}
   401  
   402  	channel, ok = c.nodeChannels[info.NodeKey2Bytes][info.ChannelID]
   403  	if ok {
   404  		channel.Capacity = info.Capacity
   405  		channel.OtherNode = info.NodeKey1Bytes
   406  	}
   407  }
   408  
   409  // getChannels returns a copy of the passed node's channels or nil if there
   410  // isn't any.
   411  func (c *GraphCache) getChannels(node route.Vertex) []*DirectedChannel {
   412  	c.mtx.RLock()
   413  	defer c.mtx.RUnlock()
   414  
   415  	channels, ok := c.nodeChannels[node]
   416  	if !ok {
   417  		return nil
   418  	}
   419  
   420  	features, ok := c.nodeFeatures[node]
   421  	if !ok {
   422  		// If the features were set to nil explicitly, that's fine here.
   423  		// The router will overwrite the features of the destination
   424  		// node with those found in the invoice if necessary. But if we
   425  		// didn't yet get a node announcement we want to mimic the
   426  		// behavior of the old DB based code that would always set an
   427  		// empty feature vector instead of leaving it nil.
   428  		features = lnwire.EmptyFeatureVector()
   429  	}
   430  
   431  	toNodeCallback := func() route.Vertex {
   432  		return node
   433  	}
   434  
   435  	i := 0
   436  	channelsCopy := make([]*DirectedChannel, len(channels))
   437  	for _, channel := range channels {
   438  		// We need to copy the channel and policy to avoid it being
   439  		// updated in the cache if the path finding algorithm sets
   440  		// fields on it (currently only the ToNodeFeatures of the
   441  		// policy).
   442  		channelCopy := channel.DeepCopy()
   443  		if channelCopy.InPolicy != nil {
   444  			channelCopy.InPolicy.ToNodePubKey = toNodeCallback
   445  			channelCopy.InPolicy.ToNodeFeatures = features
   446  		}
   447  
   448  		channelsCopy[i] = channelCopy
   449  		i++
   450  	}
   451  
   452  	return channelsCopy
   453  }
   454  
   455  // ForEachChannel invokes the given callback for each channel of the given node.
   456  func (c *GraphCache) ForEachChannel(node route.Vertex,
   457  	cb func(channel *DirectedChannel) error) error {
   458  
   459  	// Obtain a copy of the node's channels. We need do this in order to
   460  	// avoid deadlocks caused by interaction with the graph cache, channel
   461  	// state and the graph database from multiple goroutines. This snapshot
   462  	// is only used for path finding where being stale is acceptable since
   463  	// the real world graph and our representation may always become
   464  	// slightly out of sync for a short time and the actual channel state
   465  	// is stored separately.
   466  	channels := c.getChannels(node)
   467  	for _, channel := range channels {
   468  		if err := cb(channel); err != nil {
   469  			return err
   470  		}
   471  
   472  	}
   473  
   474  	return nil
   475  }
   476  
   477  // ForEachNode iterates over the adjacency list of the graph, executing the
   478  // call back for each node and the set of channels that emanate from the given
   479  // node.
   480  //
   481  // NOTE: This method should be considered _read only_, the channels or nodes
   482  // passed in MUST NOT be modified.
   483  func (c *GraphCache) ForEachNode(cb func(node route.Vertex,
   484  	channels map[uint64]*DirectedChannel) error) error {
   485  
   486  	c.mtx.RLock()
   487  	defer c.mtx.RUnlock()
   488  
   489  	for node, channels := range c.nodeChannels {
   490  		// We don't make a copy here since this is a read-only RPC
   491  		// call. We also don't need the node features either for this
   492  		// call.
   493  		if err := cb(node, channels); err != nil {
   494  			return err
   495  		}
   496  	}
   497  
   498  	return nil
   499  }
   500  
   501  // GetFeatures returns the features of the node with the given ID. If no
   502  // features are known for the node, an empty feature vector is returned.
   503  func (c *GraphCache) GetFeatures(node route.Vertex) *lnwire.FeatureVector {
   504  	c.mtx.RLock()
   505  	defer c.mtx.RUnlock()
   506  
   507  	features, ok := c.nodeFeatures[node]
   508  	if !ok || features == nil {
   509  		// The router expects the features to never be nil, so we return
   510  		// an empty feature set instead.
   511  		return lnwire.EmptyFeatureVector()
   512  	}
   513  
   514  	return features
   515  }