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:
parent
eb73d14a92
commit
3ec4eeb5e2
@ -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
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
|
||||
|
Loading…
Reference in New Issue
Block a user