lnrpc: add betweenness centrality to GetNodeMetrics (new RPC call)

This commit extends the RPC interface with GetNodeMetrics will contain
all graph node metrics in the future. Currently only holds betweennes
centrality per node.
This commit is contained in:
Andras Banki-Horvath 2020-03-19 11:14:28 +01:00
parent 3fe9c70722
commit 7e50997bb4
7 changed files with 1222 additions and 780 deletions

View File

@ -2974,6 +2974,31 @@ func describeGraph(ctx *cli.Context) error {
return nil
}
var getNodeMetricsCommand = cli.Command{
Name: "getnodemetrics",
Category: "Graph",
Description: "Prints out node metrics calculated from the current graph",
Usage: "Get node metrics.",
Action: actionDecorator(getNodeMetrics),
}
func getNodeMetrics(ctx *cli.Context) error {
client, cleanUp := getClient(ctx)
defer cleanUp()
req := &lnrpc.NodeMetricsRequest{
Types: []lnrpc.NodeMetricType{lnrpc.NodeMetricType_BETWEENNESS_CENTRALITY},
}
nodeMetrics, err := client.GetNodeMetrics(context.Background(), req)
if err != nil {
return err
}
printRespJSON(nodeMetrics)
return nil
}
var listPaymentsCommand = cli.Command{
Name: "listpayments",
Category: "Payments",

View File

@ -282,6 +282,7 @@ func main() {
closedChannelsCommand,
listPaymentsCommand,
describeGraphCommand,
getNodeMetricsCommand,
getChanInfoCommand,
getNodeInfoCommand,
queryRoutesCommand,

File diff suppressed because it is too large Load Diff

View File

@ -653,6 +653,23 @@ func request_Lightning_DescribeGraph_0(ctx context.Context, marshaler runtime.Ma
}
var (
filter_Lightning_GetNodeMetrics_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Lightning_GetNodeMetrics_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq NodeMetricsRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Lightning_GetNodeMetrics_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetNodeMetrics(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Lightning_GetChanInfo_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ChanInfoRequest
var metadata runtime.ServerMetadata
@ -1678,6 +1695,26 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_GetNodeMetrics_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_GetNodeMetrics_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_GetNodeMetrics_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Lightning_GetChanInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@ -1980,6 +2017,8 @@ var (
pattern_Lightning_DescribeGraph_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "graph"}, ""))
pattern_Lightning_GetNodeMetrics_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "graph", "nodemetrics"}, ""))
pattern_Lightning_GetChanInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "graph", "edge", "chan_id"}, ""))
pattern_Lightning_GetNodeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "graph", "node", "pub_key"}, ""))
@ -2064,6 +2103,8 @@ var (
forward_Lightning_DescribeGraph_0 = runtime.ForwardResponseMessage
forward_Lightning_GetNodeMetrics_0 = runtime.ForwardResponseMessage
forward_Lightning_GetChanInfo_0 = runtime.ForwardResponseMessage
forward_Lightning_GetNodeInfo_0 = runtime.ForwardResponseMessage

View File

@ -641,6 +641,17 @@ service Lightning {
};
}
/** lncli: `getnodemetrics`
GetNodeMetrics returns node metrics calculated from the graph. Currently
the only supported metric is betweenness centrality of individual nodes.
*/
rpc GetNodeMetrics (NodeMetricsRequest) returns (NodeMetricsResponse) {
option (google.api.http) = {
get: "/v1/graph/nodemetrics"
};
}
/** lncli: `getchaninfo`
GetChanInfo returns the latest authenticated network announcement for the
given channel identified by its channel ID: an 8-byte integer which
@ -2516,6 +2527,34 @@ message ChannelGraph {
repeated ChannelEdge edges = 2;
}
enum NodeMetricType {
BETWEENNESS_CENTRALITY = 0;
}
message NodeMetricsRequest {
/// The requesteded node metrics.
repeated NodeMetricType types = 1;
}
message NodeMetricsResponse {
/**
Betweenness centrality is the sum of the ratio of shortest paths that pass
through the node for each pair of nodes in the graph (not counting paths
starting or ending at this node).
Map of node pubkey to betweenness centrality of the node. Normalized
values are in the [0,1] closed interval.
*/
map<string, FloatValue> betweenness_centrality = 1;
}
message FloatValue {
/// Arbitrary float value.
double value = 1;
/// The value normalized to [0,1] or [-1,1].
double normalized_value = 2;
}
message ChanInfoRequest {
/**
The unique channel ID for the channel. The first 3 bytes are the block

View File

@ -718,6 +718,39 @@
]
}
},
"/v1/graph/nodemetrics": {
"get": {
"summary": "* lncli: `getnodemetrics`\nGetNodeMetrics returns node metrics calculated from the graph. Currently\nthe only supported metric is betweenness centrality of individual nodes.",
"operationId": "GetNodeMetrics",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/lnrpcNodeMetricsResponse"
}
}
},
"parameters": [
{
"name": "types",
"description": "/ The requesteded node metrics.",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "string",
"enum": [
"BETWEENNESS_CENTRALITY"
]
},
"collectionFormat": "multi"
}
],
"tags": [
"Lightning"
]
}
},
"/v1/graph/routes/{pub_key}/{amt}": {
"get": {
"summary": "* lncli: `queryroutes`\nQueryRoutes attempts to query the daemon's Channel Router for a possible\nroute to a target destination capable of carrying a specific amount of\nsatoshis. The returned route contains the full details required to craft and\nsend an HTLC, also including the necessary information that should be\npresent within the Sphinx packet encapsulated within the HTLC.",
@ -2684,6 +2717,21 @@
}
}
},
"lnrpcFloatValue": {
"type": "object",
"properties": {
"value": {
"type": "number",
"format": "double",
"description": "/ Arbitrary float value."
},
"normalized_value": {
"type": "number",
"format": "double",
"description": "/ The value normalized to [0,1] or [-1,1]."
}
}
},
"lnrpcForwardingEvent": {
"type": "object",
"properties": {
@ -3574,6 +3622,25 @@
}
}
},
"lnrpcNodeMetricType": {
"type": "string",
"enum": [
"BETWEENNESS_CENTRALITY"
],
"default": "BETWEENNESS_CENTRALITY"
},
"lnrpcNodeMetricsResponse": {
"type": "object",
"properties": {
"betweenness_centrality": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/lnrpcFloatValue"
},
"description": "*\nBetweenness centrality is the sum of the ratio of shortest paths that pass\nthrough the node for each pair of nodes in the graph (not counting paths\nstarting or ending at this node).\nMap of node pubkey to betweenness centrality of the node. Normalized\nvalues are in the [0,1] closed interval."
}
}
},
"lnrpcNodePair": {
"type": "object",
"properties": {

View File

@ -354,6 +354,10 @@ func mainRPCServerPermissions() map[string][]bakery.Op {
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/GetNodeMetrics": {{
Entity: "info",
Action: "read",
}},
"/lnrpc.Lightning/GetChanInfo": {{
Entity: "info",
Action: "read",
@ -4561,14 +4565,16 @@ func (r *rpcServer) DescribeGraph(ctx context.Context,
nodeAddrs = append(nodeAddrs, nodeAddr)
}
resp.Nodes = append(resp.Nodes, &lnrpc.LightningNode{
lnNode := &lnrpc.LightningNode{
LastUpdate: uint32(node.LastUpdate.Unix()),
PubKey: hex.EncodeToString(node.PubKeyBytes[:]),
Addresses: nodeAddrs,
Alias: node.Alias,
Color: routing.EncodeHexColor(node.Color),
Features: invoicesrpc.CreateRPCFeatures(node.Features),
})
}
resp.Nodes = append(resp.Nodes, lnNode)
return nil
})
@ -4657,6 +4663,57 @@ func marshalDbEdge(edgeInfo *channeldb.ChannelEdgeInfo,
return edge
}
// GetNodeMetrics returns all available node metrics calculated from the
// current channel graph.
func (r *rpcServer) GetNodeMetrics(ctx context.Context,
req *lnrpc.NodeMetricsRequest) (*lnrpc.NodeMetricsResponse, error) {
// Get requested metric types.
getCentrality := false
for _, t := range req.Types {
if t == lnrpc.NodeMetricType_BETWEENNESS_CENTRALITY {
getCentrality = true
}
}
// Only centrality can be requested for now.
if !getCentrality {
return nil, nil
}
resp := &lnrpc.NodeMetricsResponse{
BetweennessCentrality: make(map[string]*lnrpc.FloatValue),
}
// Obtain the pointer 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()
// Calculate betweenness centrality if requested. Note that depending on the
// graph size, this may take up to a few minutes.
channelGraph := autopilot.ChannelGraphFromDatabase(graph)
centralityMetric := autopilot.NewBetweennessCentralityMetric()
if err := centralityMetric.Refresh(channelGraph); err != nil {
return nil, err
}
// Fill normalized and non normalized centrality.
centrality := centralityMetric.GetMetric(true)
for nodeID, val := range centrality {
resp.BetweennessCentrality[hex.EncodeToString(nodeID[:])] = &lnrpc.FloatValue{
NormalizedValue: val,
}
}
centrality = centralityMetric.GetMetric(false)
for nodeID, val := range centrality {
resp.BetweennessCentrality[hex.EncodeToString(nodeID[:])].Value = val
}
return resp, nil
}
// GetChanInfo 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