cmd/lncli: add ability to render graph to 'describegraph'

This commit adds an ability to render the channel graph as returned by
the ‘displaygraph’ command. The rendering of the graph itself is
carried about the by the ‘dot’ command which eventually calls out to
graphviz.

Currently the graph is always saved to the same file in the local
directory, but in a later commit the location of the file will be made
configurable.

Finally, the attributes sent to the ‘dot’ command used to render the
graph are still a bit in flux. The parameters will likely be tuned once
the channel graph on testnet grows a bit more.
This commit is contained in:
Olaoluwa Osuntokun 2017-01-23 20:32:17 -08:00
parent eb73d14a92
commit 3ec4eeb5e2
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
3 changed files with 127 additions and 6 deletions

@ -6,11 +6,16 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"strconv"
"strings"
"github.com/awalterschulze/gographviz"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcutil"
"github.com/urfave/cli"
"golang.org/x/net/context"
)
@ -762,7 +767,13 @@ var DescribeGraphCommand = cli.Command{
Name: "describegraph",
Description: "prints a human readable version of the known channel " +
"graph from the PoV of the node",
Usage: "describegraph",
Usage: "describegraph",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "draw",
Usage: "If true, then an image of graph will be generated and displayed. The generated image is stored within the current directory with a file name of 'graph.png'",
},
},
Action: describeGraph,
}
@ -776,10 +787,114 @@ func describeGraph(ctx *cli.Context) error {
return err
}
// If the draw flag is on, then we'll use the 'dot' command to create a
// visualization of the graph itself.
if ctx.Bool("draw") {
return drawChannelGraph(graph)
}
printRespJson(graph)
return nil
}
func drawChannelGraph(graph *lnrpc.ChannelGraph) error {
// First we'll create a temporary file that we'll write the compiled
// string that describes our graph in the dot format to.
tempDotFile, err := ioutil.TempFile("", "")
if err != nil {
return err
}
defer os.Remove(tempDotFile.Name())
// Next, we'll create (or re-create) the file that the final graph
// image will be written to.
imageFile, err := os.Create("graph.png")
if err != nil {
return err
}
// With our temporary files set up, we'll initialize the graphviz
// object that we'll use to draw our graph.
graphName := "LightningNetwork"
graphCanvas := gographviz.NewGraph()
graphCanvas.SetName(graphName)
graphCanvas.SetDir(true)
// For each node within the graph, we'll add a new vertex to the graph.
for _, node := range graph.Nodes {
// Rather than using the entire hex-encoded string, we'll only
// use the first 10 characters. We also add a prefix of "Z" as
// graphviz is unable to parse the compressed pubkey as a
// non-integer.
//
// TODO(roasbeef): should be able to get around this?
nodeID := "Z" + node.PubKey[:10]
graphCanvas.AddNode(graphName, nodeID, gographviz.Attrs{})
}
// Similarly, for each edge we'll add an edge between the corresponding
// nodes added to the graph above.
for _, edge := range graph.Edges {
// Once again, we add a 'Z' prefix so we're compliant with the
// dot grammar.
src := "Z" + edge.Node1Pub[:10]
dest := "Z" + edge.Node2Pub[:10]
// The weight for our edge will be the total capacity of the
// channel, in BTC.
amt := btcutil.Amount(edge.Capacity).ToBTC()
edgeWeight := strconv.FormatFloat(amt, 'f', -1, 64)
// The label for each edge will simply be a truncated version
// of it's channel ID.
edgeLabel := strconv.FormatUint(edge.ChannelId, 10)[:10]
// We'll only draw an edge from a source to a destination if
// the source is currently advertising a route in that
// direction.
if edge.Node1Policy.TimeLockDelta != 0 {
graphCanvas.AddEdge(src, dest, true, gographviz.Attrs{
"weight": edgeWeight,
"label": edgeLabel,
})
}
if edge.Node2Policy.TimeLockDelta != 0 {
graphCanvas.AddEdge(dest, src, true, gographviz.Attrs{
"weight": edgeWeight,
"label": edgeLabel,
})
}
}
// With the declarative generation of the graph complete, we now write
// the dot-string description of the graph
graphDotString := graphCanvas.String()
if _, err := tempDotFile.WriteString(graphDotString); err != nil {
return err
}
if err := tempDotFile.Sync(); err != nil {
return err
}
// Once our dot file has been written to disk, we can use the dot
// command itself to generate the drawn rendering of the graph
// described.
drawCmd := exec.Command("dot", "-T"+"png", "-o"+imageFile.Name(),
tempDotFile.Name())
if err := drawCmd.Run(); err != nil {
return err
}
// Finally, we'll open the drawn graph to display to the user.
openCmd := exec.Command("open", imageFile.Name())
if err := openCmd.Run(); err != nil {
return err
}
return nil
}
var ListPaymentsCommand = cli.Command{
Name: "listpayments",
Usage: "listpayments",

13
glide.lock generated

@ -1,5 +1,5 @@
hash: 63c8e025e68860690ecd8c0f5d6a2ce0c20aae601f23add3cb41d702f07c2980
updated: 2017-01-16T17:29:52.63308745-08:00
hash: a292fd48b31862c25e68915190bd53af1c2a5138151e45a38ffa7f192b8456ac
updated: 2017-01-23T20:26:07.380822159-08:00
imports:
- name: github.com/aead/chacha20
version: 7e1038a97ad08a9a16cb88ed7a6778b366ba4d99
@ -7,6 +7,13 @@ imports:
- chacha
- name: github.com/aead/poly1305
version: 7ab7663051fa9fa57de391f966a859e781d48b0a
- name: github.com/awalterschulze/gographviz
version: 761fd5fbb34e4c2c138c280395b65b48e4ff5a53
subpackages:
- ast
- parser
- scanner
- token
- name: github.com/boltdb/bolt
version: 583e8937c61f1af6513608ccc75c97b6abdf4ff9
- name: github.com/btcsuite/bolt
@ -37,8 +44,6 @@ imports:
version: 31079b6807923eb23992c421b114992b95131b55
- name: github.com/codahale/chacha20
version: ec07b4f69a3f70b1dd2a8ad77230deb1ba5d6953
- name: github.com/codahale/chacha20poly1305
version: f8a5c48301822c3d7dd26d78e68ea2968db0ab20
- name: github.com/davecgh/go-spew
version: 346938d642f2ec3594ed81d874461961cd0faa76
subpackages:

@ -7,7 +7,6 @@ import:
- package: github.com/btcsuite/go-flags
- package: github.com/btcsuite/seelog
version: ^2.1.0
- package: github.com/codahale/chacha20poly1305
- package: github.com/davecgh/go-spew
subpackages:
- spew
@ -61,3 +60,5 @@ import:
- package: github.com/aead/chacha20
- package: github.com/go-errors/errors
- package: github.com/tv42/zbase32
- package: github.com/awalterschulze/gographviz
version: ^1.0.0