mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-18 18:17:39 +01:00
graph+lnd: add NetworkStats to GraphSource interface
so that the external graph source can be used to query network information rather than depending on the local graph DB.
This commit is contained in:
@@ -3,10 +3,12 @@ package sources
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/autopilot"
|
||||
"github.com/lightningnetwork/lnd/discovery"
|
||||
@@ -231,6 +233,133 @@ func (s *DBSource) GraphBootstrapper(_ context.Context) (
|
||||
return discovery.NewGraphBootstrapper(chanGraph)
|
||||
}
|
||||
|
||||
// NetworkStats returns statistics concerning the current state of the known
|
||||
// channel graph within the network.
|
||||
//
|
||||
// NOTE: this is part of the GraphSource interface.
|
||||
func (s *DBSource) NetworkStats(_ context.Context) (*models.NetworkStats,
|
||||
error) {
|
||||
|
||||
var (
|
||||
numNodes uint32
|
||||
numChannels uint32
|
||||
maxChanOut uint32
|
||||
totalNetworkCapacity btcutil.Amount
|
||||
minChannelSize btcutil.Amount = math.MaxInt64
|
||||
maxChannelSize btcutil.Amount
|
||||
medianChanSize btcutil.Amount
|
||||
)
|
||||
|
||||
// We'll use this map to de-duplicate channels during our traversal.
|
||||
// This is needed since channels are directional, so there will be two
|
||||
// edges for each channel within the graph.
|
||||
seenChans := make(map[uint64]struct{})
|
||||
|
||||
// We also keep a list of all encountered capacities, in order to
|
||||
// calculate the median channel size.
|
||||
var allChans []btcutil.Amount
|
||||
|
||||
// We'll run through all the known nodes in the within our view of the
|
||||
// network, tallying up the total number of nodes, and also gathering
|
||||
// each node so we can measure the graph diameter and degree stats
|
||||
// below.
|
||||
err := s.db.ForEachNodeCached(func(node route.Vertex,
|
||||
edges map[uint64]*graphdb.DirectedChannel) error {
|
||||
|
||||
// Increment the total number of nodes with each iteration.
|
||||
numNodes++
|
||||
|
||||
// For each channel we'll compute the out degree of each node,
|
||||
// and also update our running tallies of the min/max channel
|
||||
// capacity, as well as the total channel capacity. We pass
|
||||
// through the DB transaction from the outer view so we can
|
||||
// re-use it within this inner view.
|
||||
var outDegree uint32
|
||||
for _, edge := range edges {
|
||||
// Bump up the out degree for this node for each
|
||||
// channel encountered.
|
||||
outDegree++
|
||||
|
||||
// If we've already seen this channel, then we'll
|
||||
// return early to ensure that we don't double-count
|
||||
// stats.
|
||||
if _, ok := seenChans[edge.ChannelID]; ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare the capacity of this channel against the
|
||||
// running min/max to see if we should update the
|
||||
// extrema.
|
||||
chanCapacity := edge.Capacity
|
||||
if chanCapacity < minChannelSize {
|
||||
minChannelSize = chanCapacity
|
||||
}
|
||||
if chanCapacity > maxChannelSize {
|
||||
maxChannelSize = chanCapacity
|
||||
}
|
||||
|
||||
// Accumulate the total capacity of this channel to the
|
||||
// network wide-capacity.
|
||||
totalNetworkCapacity += chanCapacity
|
||||
|
||||
numChannels++
|
||||
|
||||
seenChans[edge.ChannelID] = struct{}{}
|
||||
allChans = append(allChans, edge.Capacity)
|
||||
}
|
||||
|
||||
// Finally, if the out degree of this node is greater than what
|
||||
// we've seen so far, update the maxChanOut variable.
|
||||
if outDegree > maxChanOut {
|
||||
maxChanOut = outDegree
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the median.
|
||||
medianChanSize = autopilot.Median(allChans)
|
||||
|
||||
// If we don't have any channels, then reset the minChannelSize to zero
|
||||
// to avoid outputting NaN in encoded JSON.
|
||||
if numChannels == 0 {
|
||||
minChannelSize = 0
|
||||
}
|
||||
|
||||
// Graph diameter.
|
||||
channelGraph := autopilot.ChannelGraphFromCachedDatabase(s.db)
|
||||
simpleGraph, err := autopilot.NewSimpleGraph(channelGraph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start := time.Now()
|
||||
diameter := simpleGraph.DiameterRadialCutoff()
|
||||
|
||||
log.Infof("Elapsed time for diameter (%d) calculation: %v", diameter,
|
||||
time.Since(start))
|
||||
|
||||
// Query the graph for the current number of zombie channels.
|
||||
numZombies, err := s.db.NumZombies()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.NetworkStats{
|
||||
Diameter: diameter,
|
||||
MaxChanOut: maxChanOut,
|
||||
NumNodes: numNodes,
|
||||
NumChannels: numChannels,
|
||||
TotalNetworkCapacity: totalNetworkCapacity,
|
||||
MinChanSize: minChannelSize,
|
||||
MaxChanSize: maxChannelSize,
|
||||
MedianChanSize: medianChanSize,
|
||||
NumZombies: numZombies,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// kvdbRTx is an implementation of graphdb.RTx backed by a KVDB database read
|
||||
// transaction.
|
||||
type kvdbRTx struct {
|
||||
|
||||
Reference in New Issue
Block a user