Extracting topology data from LND

In previous story, I described how I installed a bitcoin lightning network node at home, running LND on a cheap Raspberry Pi3.

lncli provides a useful command to export the the network topology. We can simply run:

$ lncli describegraph > /tmp/describegraph.json

to obtain a json that contains the topology of the lightning network as seen from the current lnd node.

The format of describegraph.json is pretty simple and self-explanatory:

{

"nodes": [

{

"last_update": 1539310922,

"pub_key": "02004c625d622245606a1ea2c1c69cfb4516b703b47945a3647713c05fe4aaeb1c",

"alias": "LivingRoomOfSatoshi.com (LND) 2",

"addresses": [

{

"network": "tcp",

"addr": "172.81.178.151:9735"

}

],

"color": "#3399ff"

},

...

...

],

"edges": [

{

"channel_id": "595923207643922432",

"chan_point": "dd0d25aadec1ff27a7215834bb3660cb2d0c5c030f3a8a27096e243313648ae2:0",

"last_update": 1540314360,

"node1_pub": "02cdf83ef8e45908b1092125d25c68dcec7751ca8d39f557775cd842e5bc127469",

"node2_pub": "032c4b954f0f171b694b5e8e8323589e54196b48cf2efc27692513a360cb11d76f",

"capacity": "500000",

"node1_policy": {

"time_lock_delta": 144,

"min_htlc": "1000",

"fee_base_msat": "1000",

"fee_rate_milli_msat": "1",

"disabled": false

},

"node2_policy": {

"time_lock_delta": 144,

"min_htlc": "1000",

"fee_base_msat": "1000",

"fee_rate_milli_msat": "1",

"disabled": false

}

},

...

...

]

}

We can then quickly count the number of nodes and channel:

$ cat /tmp/describegraph.json | grep “pub_key” | wc -l

877 $ cat /tmp/describegraph.json | grep “channel_id” | wc -l

2796

Import topology data in Neo4j

Installing Neo4jDesktop

What’s better than a Graph Database to explore a network topology? Let’s download Neo4jDesktop from https://neo4j.com/product/ and spin a database to import our describegraph.json

We can now create a new Graph in Neo4jDesktop, just click on Add Graph → Create a Local Graph, set the graph name, the password and click Create.

Then we need to install the APOC library (“ Awesome Procedures On Cypher”), in Neo4j Desktop: go to the Manage screen and the Plugins tab. Now click on the Install button in the APOC box and you’re done.

Because we need to import a local file, we need to change the default configuration: go to the Manage screen and the Settings tab and add this line:

apoc.import.file.enabled=true

Now click on Open Browser and be ready to start or exploration of the lightning network.

Import the network data

It’s now time to import the describegraph file in our Graph database. We can use the procedure load.json provided by the APOC library to load the json file and then iterate over the edges, creating 2 Node objects for each edge we found in the list, connected by a relationship LINKED . Then we can add a field alias to each Node object to better identify them.

CALL apoc.load.json(“file:///full/path/to/describegraph.json”) YIELD value AS data

UNWIND data.edges as edge

MERGE (n1:Node {id:edge.node1_pub} )

MERGE (n2:Node {id:edge.node2_pub} )

CREATE (n1)-[:LINKED {capacity:edge.capacity}]->(n2) CALL apoc.load.json(“file:///full/path/to/describegraph.json”) YIELD value AS data

UNWIND data.nodes as node

MERGE (n:Node {id:node.pub_key} )

SET n.alias = node.alias

Finally we can add an index on the field alias to speed up the queries.

CREATE INDEX ON :Node(alias)

Exploring the topology

We are now ready to use the powerful query engine of Neo4j to discover some interesting facts about the network.

Number of Nodes and Channel

First, let’s see how many nodes and channels we know about. This is an easy query, we need to count all the Nodes objects and then count all the relationships LINKED present in the graph:

MATCH (n:Node) RETURN COUNT(*) COUNT(*) 877 MATCH (n)-[r:LINKED]->() RETURN COUNT(r) COUNT(r)2796

The numbers obtained by those queries match the number obtained from the “grep” that we previously executed directly on the describegraph.json file, we we can be pretty confident that the import procedure run smooth.

According to https://1ml.com/, at the moment we have more that 3,000 nodes and more than 12,000 channels in the lightning network. So, even after almost 6 weeks of operation, my node knows about less that 1/3 of the nodes and less than 1/4 of the channels that exist in the network.

This is not surprising: mesh routing is unsolved problem in Computer Science yet.

The longest shortest path

The longest shortest path in the network in defined as the max distance between nodes, or network diameter. Neo4j is a Graph oriented DB, so it provides a shortestpath function out of the box. We can easily find all the shortest paths and get the longest:

MATCH (n1:Node), (n2:Node), route = shortestpath((n1)-[:LINKED*]-(n2))

WHERE id(n1) < id(n2)

RETURN route

ORDER BY LENGTH(route) DESC

LIMIT 1

The longest shortest path, according to the topology of the Lightning network as seen from my node.

So, according to my view of the network, the most distant nodes are 8 hops away.

The most distant nodes

We can also find the most distant nodes from a given node, for instance here are the 5 most distant nodes from my at home node, aliased SLL.

MATCH (n1:Node), (n2:Node),

route = shortestpath((n1)-[:LINKED*]-(n2))

WHERE id(n1) < id(n2) AND n1.alias = 'SLL'

RETURN route

ORDER BY LENGTH(route) DESC

LIMIT 5

The shortest paths to the 5 most distant nodes from mine

The top 10 Hubs

Let’s find the nodes with most directly connected nodes (not channels…)

MATCH (n1:Node)-[:LINKED]-(n2:Node)

RETURN n2.alias, SIZE(COLLECT(n1)) as connected

ORDER BY connected DESC

LIMIT 10

The winner is the node tady je slushovo, with 169 nodes directly linked.

Triangle Counting

An interesting query that we can run is the count of Triangles that each node is part of:

CALL algo.triangleCount.stream(‘Node’,’LINKED’,{concurrency:4})

YIELD nodeId, triangles, coefficient

RETURN algo.getNodeById(nodeId).alias AS alias, triangles, coefficient

ORDER BY triangles DESC

clustering coefficient is the likelihood that its neighbors are also connected. You can find more detail in the wikipedia page https://en.wikipedia.org/wiki/Clustering_coefficient , but simply speaking: a node a has clustering coefficient 1 if every neighbor connected to it is also connected to every other node within the neighborhood, and 0 if no node that is connected to it connects to any other node that is connected to it.

The top 10 nodes by number of triangles seam to have a local clustering coefficient lesser than 10%

In a perfect tia

We can also calculate the network average clustering coefficient.

CALL algo.triangleCount(‘Node’, ‘LINKED’,

{concurrency:4, write:true, writeProperty:’triangles’,clusteringCoefficientProperty:’coefficient’})

YIELD nodeCount, triangleCount, averageClusteringCoefficient;

So, my node knows about a total of 877 nodes in the network, meshed in 2045 triangles, and the average of the local clustering coefficient is about 0.123.

Conclusions

I hope you had fun discovering some interesting facts the lightning network topology.

Feel free to suggest more interesting queries to run :)