rpcserver: implement new set of network/routing RPC's
This commit is contained in:
parent
ea6f6d6069
commit
1210640e87
299
rpcserver.go
299
rpcserver.go
@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@ -1151,30 +1152,285 @@ func (r *rpcServer) GetTransactions(context.Context,
|
||||
return txDetails, nil
|
||||
}
|
||||
|
||||
// ShowRoutingTable returns a table-formatted dump of the known routing
|
||||
// topology from the PoV of the source node.
|
||||
func (r *rpcServer) ShowRoutingTable(ctx context.Context,
|
||||
in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) {
|
||||
// DescribeGraph returns a description of the latest graph state from the PoV
|
||||
// of the node. The graph information is partitioned into two components: all
|
||||
// the nodes/vertexes, and all the edges that connect the vertexes themselves.
|
||||
// As this is a directed graph, the edges also contain the node directional
|
||||
// specific routing policy which includes: the time lock delta, fee
|
||||
// information, etc.
|
||||
func (r *rpcServer) DescribeGraph(context.Context,
|
||||
*lnrpc.ChannelGraphRequest) (*lnrpc.ChannelGraph, error) {
|
||||
|
||||
rpcsLog.Debugf("[ShowRoutingTable]")
|
||||
resp := &lnrpc.ChannelGraph{}
|
||||
|
||||
rtCopy := r.server.routingMgr.GetRTCopy()
|
||||
// Obtain the pinter to the global singleton channel graph, this will
|
||||
// provide a consistent view of the graph due to bolt db's
|
||||
// transactional model.
|
||||
graph := r.server.chanDB.ChannelGraph()
|
||||
|
||||
var channels []*lnrpc.RoutingTableLink
|
||||
for _, channel := range rtCopy.AllChannels() {
|
||||
channels = append(channels,
|
||||
&lnrpc.RoutingTableLink{
|
||||
Id1: hex.EncodeToString(channel.Src.ToByte()),
|
||||
Id2: hex.EncodeToString(channel.Tgt.ToByte()),
|
||||
Outpoint: channel.Id.String(),
|
||||
Capacity: channel.Info.Cpt,
|
||||
Weight: channel.Info.Wgt,
|
||||
},
|
||||
)
|
||||
// First iterate through all the known nodes (connected or unconnected
|
||||
// within the graph), collating their current state into the RPC
|
||||
// response.
|
||||
err := graph.ForEachNode(func(node *channeldb.LightningNode) error {
|
||||
resp.Nodes = append(resp.Nodes, &lnrpc.LightningNode{
|
||||
LastUpdate: uint32(node.LastUpdate.Unix()),
|
||||
PubKey: hex.EncodeToString(node.PubKey.SerializeCompressed()),
|
||||
Address: node.Address.String(),
|
||||
Alias: node.Alias,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lnrpc.ShowRoutingTableResponse{
|
||||
Channels: channels,
|
||||
// Next, for each active channel we know of within the graph, create a
|
||||
// similar response which details both the edge information as well as
|
||||
// the routing policies of th nodes connecting the two edges.
|
||||
err = graph.ForEachChannel(func(c1, c2 *channeldb.ChannelEdge) error {
|
||||
edge := marshalDbEdge(c1, c2)
|
||||
resp.Edges = append(resp.Edges, edge)
|
||||
return nil
|
||||
})
|
||||
if err != nil && err != channeldb.ErrGraphNoEdgesFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func marshalDbEdge(c1, c2 *channeldb.ChannelEdge) *lnrpc.ChannelEdge {
|
||||
node1Pub := c2.Node.PubKey.SerializeCompressed()
|
||||
node2Pub := c1.Node.PubKey.SerializeCompressed()
|
||||
|
||||
edge := &lnrpc.ChannelEdge{
|
||||
ChannelId: c1.ChannelID,
|
||||
ChanPoint: c1.ChannelPoint.String(),
|
||||
LastUpdate: uint32(c1.LastUpdate.Unix()),
|
||||
Node1Pub: hex.EncodeToString(node1Pub),
|
||||
Node2Pub: hex.EncodeToString(node2Pub),
|
||||
Capacity: int64(c1.Capacity),
|
||||
}
|
||||
|
||||
edge.Node1Policy = &lnrpc.RoutingPolicy{
|
||||
TimeLockDelta: uint32(c1.Expiry),
|
||||
MinHtlc: int64(c1.MinHTLC),
|
||||
FeeBaseMsat: int64(c1.FeeBaseMSat),
|
||||
FeeRateMilliMsat: int64(c1.FeeProportionalMillionths),
|
||||
}
|
||||
|
||||
edge.Node2Policy = &lnrpc.RoutingPolicy{
|
||||
TimeLockDelta: uint32(c2.Expiry),
|
||||
MinHtlc: int64(c2.MinHTLC),
|
||||
FeeBaseMsat: int64(c2.FeeBaseMSat),
|
||||
FeeRateMilliMsat: int64(c2.FeeProportionalMillionths),
|
||||
}
|
||||
|
||||
return edge
|
||||
}
|
||||
|
||||
// GetChainInfo returns the latest authenticated network announcement for the
|
||||
// given channel identified by its channel ID: an 8-byte integer which uniquely
|
||||
// identifies the location of transaction's funding output within the block
|
||||
// chain.
|
||||
func (r *rpcServer) GetChanInfo(_ context.Context, in *lnrpc.ChanInfoRequest) (*lnrpc.ChannelEdge, error) {
|
||||
graph := r.server.chanDB.ChannelGraph()
|
||||
|
||||
edge1, edge2, err := graph.FetchChannelEdgesByID(in.ChanId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert the database's edge format into the network/RPC edge format
|
||||
// which couples the edge itself along with the directional node
|
||||
// routing policies of each node involved within the channel.
|
||||
channelEdge := marshalDbEdge(edge1, edge2)
|
||||
|
||||
return channelEdge, nil
|
||||
}
|
||||
|
||||
// GetNodeInfo returns the latest advertised and aggregate authenticated
|
||||
// channel information for the specified node identified by its public key.
|
||||
func (r *rpcServer) GetNodeInfo(_ context.Context, in *lnrpc.NodeInfoRequest) (*lnrpc.NodeInfo, error) {
|
||||
|
||||
graph := r.server.chanDB.ChannelGraph()
|
||||
|
||||
// First, parse the hex-encoded public key into a full in-memory public
|
||||
// key object we can work with for querying.
|
||||
pubKeyBytes, err := hex.DecodeString(in.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the public key decoded, attempt to fetch the node corresponding
|
||||
// to this public key. If the node cannot be found, then an error will
|
||||
// be returned.
|
||||
node, err := graph.FetchLightningNode(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the node obtained, we'll now iterate through all its out going
|
||||
// edges to gather some basic statistics about its out going channels.
|
||||
var (
|
||||
numChannels uint32
|
||||
totalCapcity btcutil.Amount
|
||||
)
|
||||
if err := node.ForEachChannel(nil, func(edge *channeldb.ChannelEdge) error {
|
||||
numChannels++
|
||||
totalCapcity += edge.Capacity
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &lnrpc.NodeInfo{
|
||||
Node: &lnrpc.LightningNode{
|
||||
LastUpdate: uint32(node.LastUpdate.Unix()),
|
||||
PubKey: in.PubKey,
|
||||
Address: node.Address.String(),
|
||||
Alias: node.Alias,
|
||||
},
|
||||
NumChannels: numChannels,
|
||||
TotalCapacity: int64(totalCapcity),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// QueryRoute attempts to query the daemons' Channel Router for a possible
|
||||
// route to a target destination capable of carrying a specific amount of
|
||||
// satoshis within the route's flow. The retuned route contains the full
|
||||
// details required to craft and send an HTLC, also including the necessary
|
||||
// information that should be present within the Sphinx packet encapsualted
|
||||
// within the HTLC.
|
||||
//
|
||||
// TODO(roasbeef): should return a slice of routes in reality
|
||||
// * create separate PR to send based on well formatted route
|
||||
func (r *rpcServer) QueryRoute(_ context.Context, in *lnrpc.RouteRequest) (*lnrpc.Route, error) {
|
||||
// First parse the hex-encdoed public key into a full public key objet
|
||||
// we can properly manipulate.
|
||||
pubKeyBytes, err := hex.DecodeString(in.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Query the channel router for a possible path to the destination that
|
||||
// can carry `in.Amt` satoshis _including_ the total fee required on
|
||||
// the route.
|
||||
route, err := r.server.chanRouter.FindRoute(pubKey,
|
||||
btcutil.Amount(in.Amt))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If a route exsits within the network that is able to support our
|
||||
// request, then we'll convert the result into the format required by
|
||||
// the RPC system.
|
||||
resp := &lnrpc.Route{
|
||||
TotalTimeLock: route.TotalTimeLock,
|
||||
TotalFees: int64(route.TotalFees),
|
||||
TotalAmt: int64(route.TotalAmount),
|
||||
Hops: make([]*lnrpc.Hop, len(route.Hops)),
|
||||
}
|
||||
for i, hop := range route.Hops {
|
||||
resp.Hops[i] = &lnrpc.Hop{
|
||||
ChanId: hop.Channel.ChannelID,
|
||||
ChanCapacity: int64(hop.Channel.Capacity),
|
||||
AmtToForward: int64(hop.AmtToForward),
|
||||
Fee: int64(hop.Fee),
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetNetworkInfo returns some basic stats about the known channel graph from
|
||||
// the PoV of the node.
|
||||
func (r *rpcServer) GetNetworkInfo(context.Context, *lnrpc.NetworkInfoRequest) (*lnrpc.NetworkInfo, error) {
|
||||
|
||||
graph := r.server.chanDB.ChannelGraph()
|
||||
|
||||
var (
|
||||
numNodes uint32
|
||||
numChannels uint32
|
||||
maxChanOut uint32
|
||||
totalNetworkCapacity btcutil.Amount
|
||||
minChannelSize btcutil.Amount = math.MaxInt64
|
||||
maxChannelSize btcutil.Amount
|
||||
)
|
||||
|
||||
// TODO(roasbeef): ideally all below is completed in a single
|
||||
// transaction
|
||||
|
||||
// First 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 diamter and degree stats
|
||||
// below.
|
||||
var nodes []*channeldb.LightningNode
|
||||
if err := graph.ForEachNode(func(node *channeldb.LightningNode) error {
|
||||
numNodes++
|
||||
nodes = append(nodes, node)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With all the nodes gathered, we can now perform a basic traversal to
|
||||
// ascertain the graph's diameter, and also the max out-degree of a
|
||||
// node.
|
||||
for _, node := range nodes {
|
||||
var outDegree uint32
|
||||
err := node.ForEachChannel(nil, func(c *channeldb.ChannelEdge) error {
|
||||
outDegree++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if outDegree > maxChanOut {
|
||||
outDegree = maxChanOut
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we traverse each channel visiting both channel edges at
|
||||
// once to avoid double counting any stats we're attempting to gather.
|
||||
if err := graph.ForEachChannel(func(c1, c2 *channeldb.ChannelEdge) error {
|
||||
chanCapacity := c1.Capacity
|
||||
|
||||
if chanCapacity < minChannelSize {
|
||||
minChannelSize = chanCapacity
|
||||
}
|
||||
if chanCapacity > maxChannelSize {
|
||||
maxChannelSize = chanCapacity
|
||||
}
|
||||
|
||||
totalNetworkCapacity += chanCapacity
|
||||
|
||||
numChannels++
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): also add oldest channel?
|
||||
return &lnrpc.NetworkInfo{
|
||||
MaxOutDegree: maxChanOut,
|
||||
AvgOutDegree: float64(numChannels) / float64(numNodes),
|
||||
NumNodes: numNodes,
|
||||
NumChannels: numChannels,
|
||||
TotalNetworkCapacity: int64(totalNetworkCapacity),
|
||||
AvgChannelSize: float64(totalNetworkCapacity) / float64(numChannels),
|
||||
MinChannelSize: int64(minChannelSize),
|
||||
MaxChannelSize: int64(maxChannelSize),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -1218,3 +1474,8 @@ func (r *rpcServer) DeleteAllPayments(context.Context,
|
||||
resp := &lnrpc.DeleteAllPaymentsResponse{}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// SetAlias...
|
||||
func (r *rpcServer) SetAlias(context.Context, *lnrpc.SetAliasRequest) (*lnrpc.SetAliasResponse, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user