lncli: Add graphical output of routing table

LIGHT-131, LIGHT-140, LIGHT-138
`lncli showroutingtable` may output routing table as image.
Use graphviz for graph rendering.
Add explicit version dependency for tools. Add error checking.
This commit is contained in:
BitfuryLightning 2016-08-21 17:46:54 +03:00
parent d8bceb16f9
commit 2bcff188e8
8 changed files with 347 additions and 166 deletions

View File

@ -6,7 +6,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
@ -14,8 +16,10 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
"golang.org/x/net/context" "golang.org/x/net/context"
"github.com/BitfuryLightning/tools/rt" "github.com/BitfuryLightning/tools/rt"
"github.com/BitfuryLightning/tools/rt/graph/prefix_tree" "github.com/BitfuryLightning/tools/prefix_tree"
"github.com/BitfuryLightning/tools/rt/graph" "github.com/BitfuryLightning/tools/rt/graph"
"github.com/BitfuryLightning/tools/rt/visualizer"
) )
// TODO(roasbeef): cli logic for supporting both positional and unix style // TODO(roasbeef): cli logic for supporting both positional and unix style
@ -528,54 +532,212 @@ func sendPaymentCommand(ctx *cli.Context) error {
var ShowRoutingTableCommand = cli.Command{ var ShowRoutingTableCommand = cli.Command{
Name: "showroutingtable", Name: "showroutingtable",
Description: "shows routing table for a node", Description: "shows routing table for a node",
Usage: "showroutingtable [--table]", Usage: "showroutingtable text|image",
Subcommands: []cli.Command{
{
Name: "text",
Usage: "[--table|--human]",
Description: "Show routing table in textual format. By default in JSON",
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "table", Name: "table",
Usage: "Show the routing table in table format. Print only a few first symbols of id", Usage: "Print channels in routing table in table format.",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "human", Name: "human",
Usage: "Simplify output to human readable form. Output lightning_id partially. Only work with --table option.", Usage: "Print channels in routing table in table format. Output lightning_id partially - only a few first symbols which uniquelly identifies it.",
},
},
Action: showRoutingTableAsText,
},
{
Name: "image",
Usage: "[--type <IMAGE_TYPE>] [--dest OUTPUT_FILE] [--open]",
Description: "Create image with graphical representation of routing table",
Flags: []cli.Flag{
cli.StringFlag{
Name: "type",
Usage: "Type of image file. Use one of: http://www.graphviz.org/content/output-formats. Usage of this option supresses textual output",
},
cli.StringFlag{
Name: "dest",
Usage: "Specifies where to save the generated file. If don't specified use os.TempDir Usage of this option supresses textual output",
},
cli.BoolFlag{
Name: "open",
Usage: "Open generated file automatically. Uses command line \"open\" command",
},
},
Action: showRoutingTableAsImage,
}, },
}, },
Action: showRoutingTable,
} }
func showRoutingTable(ctx *cli.Context) error { func getRoutingTable(ctxb context.Context, client lnrpc.LightningClient) (*rt.RoutingTable, error) {
ctxb := context.Background()
client := getClient(ctx)
req := &lnrpc.ShowRoutingTableRequest{} req := &lnrpc.ShowRoutingTableRequest{}
resp, err := client.ShowRoutingTable(ctxb, req) resp, err := client.ShowRoutingTable(ctxb, req)
if err != nil { if err != nil {
return err return nil, err
} }
// TODO(mkl): maybe it is better to print output directly omitting
// conversion to RoutingTable. This part is not performance critical so
// I think it is ok because it enables code reuse
r := rt.NewRoutingTable() r := rt.NewRoutingTable()
for _, channel := range resp.Channels { for _, channel := range resp.Channels {
r.AddChannel( r.AddChannel(
graph.NewID(channel.Id1), graph.NewID(channel.Id1),
graph.NewID(channel.Id2), graph.NewID(channel.Id2),
graph.NewEdgeID(channel.EdgeID), graph.NewEdgeID(channel.Outpoint),
&rt.ChannelInfo{channel.Capacity, channel.Weight}, &rt.ChannelInfo{channel.Capacity, channel.Weight},
) )
} }
return r, nil
}
func showRoutingTableAsText(ctx *cli.Context) error {
ctxb := context.Background()
client := getClient(ctx)
r, err := getRoutingTable(ctxb, client)
if err != nil{ if err != nil{
fmt.Println("Can't unmarshall routing table")
return err return err
} }
if ctx.Bool("table") && ctx.Bool("human"){
return fmt.Errorf("--table and --human cannot be used at the same time")
}
if ctx.Bool("table") { if ctx.Bool("table") {
printRTAsTable(r, ctx.Bool("human")) printRTAsTable(r, false)
} else if ctx.Bool("human") {
printRTAsTable(r, true)
} else { } else {
printRTAsJSON(r) printRTAsJSON(r)
} }
return nil return nil
} }
func showRoutingTableAsImage(ctx *cli.Context) error {
ctxb := context.Background()
client := getClient(ctx)
r, err := getRoutingTable(ctxb, client)
if err != nil{
return err
}
reqGetInfo := &lnrpc.GetInfoRequest{}
respGetInfo, err := client.GetInfo(ctxb, reqGetInfo)
if err != nil {
return err
}
selfLightningId, err := hex.DecodeString(respGetInfo.LightningId)
if err != nil {
return err
}
imgType := ctx.String("type")
imgDest := ctx.String("dest")
if imgType == "" && imgDest == "" {
return fmt.Errorf("One or both of --type or --dest should be specified")
}
tempFile, err := ioutil.TempFile("", "")
if err != nil {
return err
}
var imageFile *os.File
// if the type is not specified explicitly parse the filename
if imgType == "" {
imgType = filepath.Ext(imgDest)[1:]
}
// if the filename is not specified explicitly use tempfile
if imgDest == "" {
imageFile, err = TempFileWithSuffix("", "rt_", "."+ imgType)
if err != nil {
return err
}
} else {
imageFile, err = os.Create(imgDest)
if err != nil {
return err
}
}
if _, ok := visualizer.SupportedFormatsAsMap()[imgType]; !ok {
fmt.Printf("Format: '%v' not recognized. Use one of: %v\n", imgType, visualizer.SupportedFormats())
return nil
}
// generate description graph by dot language
err = writeToTempFile(r, tempFile, selfLightningId)
if err != nil {
return err
}
err = writeToImageFile(tempFile, imageFile)
if err != nil {
return err
}
if ctx.Bool("open") {
if err := visualizer.Open(imageFile); err != nil {
return err
}
}
return nil
}
func writeToTempFile(r *rt.RoutingTable, file *os.File, self []byte) error {
slc := []graph.ID{graph.NewID(string(self))}
viz := visualizer.New(r.G, slc, nil, nil)
viz.ApplyToNode = func(s string) string { return hex.EncodeToString([]byte(s)) }
viz.ApplyToEdge = func(info interface{}) string {
if info, ok := info.(*rt.ChannelInfo); ok {
return fmt.Sprintf(`"%v"`, info.Capacity())
}
return "nil"
}
// need to call method if plan to use shortcut, autocomplete, etc
viz.BuildPrefixTree()
viz.EnableShortcut(true)
dot := viz.Draw()
_, err := file.Write([]byte(dot))
if err != nil {
return err
}
err = file.Sync()
if err != nil {
return err
}
return nil
}
func writeToImageFile(TempFile, ImageFile *os.File) error {
err := visualizer.Run("neato", TempFile, ImageFile)
if err != nil {
return err
}
err = TempFile.Close()
if err != nil {
return err
}
err = os.Remove(TempFile.Name())
if err != nil {
return err
}
err = ImageFile.Close()
if err != nil {
return err
}
return nil
}
// get around a bug in the standard library, add suffix param
func TempFileWithSuffix(dir, prefix, suffix string) (*os.File, error) {
f, err := ioutil.TempFile(dir, prefix)
if err != nil {
return nil, err
}
defer os.Remove(f.Name())
f, err = os.Create(f.Name()+suffix)
return f, err
}
// Prints routing table in human readable table format // Prints routing table in human readable table format
func printRTAsTable(r *rt.RoutingTable, humanForm bool) { func printRTAsTable(r *rt.RoutingTable, humanForm bool) {
// Minimum length of data part to which name can be shortened // Minimum length of data part to which name can be shortened
@ -589,7 +751,7 @@ func printRTAsTable(r *rt.RoutingTable, humanForm bool) {
tmpl = "%-64v %-64v %-66v %-10v %-10v\n" tmpl = "%-64v %-64v %-66v %-10v %-10v\n"
minLen = 100 minLen = 100
} }
fmt.Printf(tmpl, "ID1", "ID2", "EdgeID", "Capacity", "Weight") fmt.Printf(tmpl, "ID1", "ID2", "Outpoint", "Capacity", "Weight")
channels := r.AllChannels() channels := r.AllChannels()
if humanForm { if humanForm {
// Generate prefix tree for shortcuts // Generate prefix tree for shortcuts
@ -639,8 +801,8 @@ func printRTAsJSON(r *rt.RoutingTable) {
type ChannelDesc struct { type ChannelDesc struct {
ID1 string `json:"lightning_id1"` ID1 string `json:"lightning_id1"`
ID2 string `json:"lightning_id2"` ID2 string `json:"lightning_id2"`
EdgeId string `json:"edge_id"` EdgeId string `json:"outpoint"`
Capacity float64 `json:"capacity"` Capacity int64 `json:"capacity"`
Weight float64 `json:"weight"` Weight float64 `json:"weight"`
} }
var channels struct { var channels struct {

View File

@ -584,7 +584,7 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
// so this new channel can be utilized during path // so this new channel can be utilized during path
// finding. // finding.
chanInfo := openChan.StateSnapshot() chanInfo := openChan.StateSnapshot()
capacity := float64(chanInfo.Capacity) capacity := int64(chanInfo.Capacity)
fmsg.peer.server.routingMgr.OpenChannel( fmsg.peer.server.routingMgr.OpenChannel(
graph.NewID(chanInfo.RemoteID), graph.NewID(chanInfo.RemoteID),
graph.NewEdgeID(fundingPoint.String()), graph.NewEdgeID(fundingPoint.String()),
@ -654,7 +654,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
resCtx.reservation.FundingOutpoint, fmsg.peer.id) resCtx.reservation.FundingOutpoint, fmsg.peer.id)
// Notify the L3 routing manager of the newly active channel link. // Notify the L3 routing manager of the newly active channel link.
capacity := float64(resCtx.reservation.OurContribution().FundingAmount + capacity := int64(resCtx.reservation.OurContribution().FundingAmount +
resCtx.reservation.TheirContribution().FundingAmount) resCtx.reservation.TheirContribution().FundingAmount)
fmsg.peer.server.routingMgr.OpenChannel( fmsg.peer.server.routingMgr.OpenChannel(
graph.NewID([32]byte(fmsg.peer.lightningID)), graph.NewID([32]byte(fmsg.peer.lightningID)),

41
glide.lock generated
View File

@ -1,22 +1,31 @@
hash: 02d4ee92f22da78cae1ef93beb72f1814e12714ec4b76bc1d009769990084f83 hash: 348cab6c25a05211caed34dc711bd1cb5a51800bc87d3609127ff9271b206c42
updated: 2016-08-11T11:35:40.811837309-07:00 updated: 2016-09-02T08:34:25.843272647-04:00
imports: imports:
- name: github.com/awalterschulze/gographviz
version: cafbade2d58068c3992f12afe46742195c673d2b
subpackages:
- ast
- parser
- scanner
- token
- name: github.com/BitfuryLightning/tools - name: github.com/BitfuryLightning/tools
version: 5559e9eeb613446747dd43026ab60f63ad6eeb05 version: b36ae00916b800503504455f7afeb3159bd5ee35
subpackages: subpackages:
- routing - routing
- rt - rt
- rt/graph - rt/graph
- prefix_tree
- rt/visualizer
- pbuffer - pbuffer
- pqueue - pqueue
- name: github.com/boltdb/bolt - name: github.com/boltdb/bolt
version: dfb21201d9270c1082d5fb0f07f500311ff72f18 version: 583e8937c61f1af6513608ccc75c97b6abdf4ff9
- name: github.com/btcsuite/bolt - name: github.com/btcsuite/bolt
version: 38b9bbfde72d4b62b6a038a3adfca64c44da0133 version: da4838c39653ed69caa78c99ca68001a89eb973f
- name: github.com/btcsuite/btclog - name: github.com/btcsuite/btclog
version: f96df2375f37300305f329b8e5258764b4f19a7f version: 73889fb79bd687870312b6e40effcecffbd57d30
- name: github.com/btcsuite/fastsha256 - name: github.com/btcsuite/fastsha256
version: 302ad4db268b46f9ebda3078f6f7397f96047735 version: 637e656429416087660c84436a2a035d69d54e2e
- name: github.com/btcsuite/go-flags - name: github.com/btcsuite/go-flags
version: 6c288d648c1cc1befcb90cb5511dcacf64ae8e61 version: 6c288d648c1cc1befcb90cb5511dcacf64ae8e61
- name: github.com/btcsuite/go-socks - name: github.com/btcsuite/go-socks
@ -42,15 +51,15 @@ imports:
- name: github.com/codahale/chacha20poly1305 - name: github.com/codahale/chacha20poly1305
version: f8a5c48301822c3d7dd26d78e68ea2968db0ab20 version: f8a5c48301822c3d7dd26d78e68ea2968db0ab20
- name: github.com/davecgh/go-spew - name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d version: 6cf5744a041a0022271cefed95ba843f6d87fd51
subpackages: subpackages:
- spew - spew
- name: github.com/golang/protobuf - name: github.com/golang/protobuf
version: 2c1988e8c18d14b142c0b472624f71647cf39adb version: 1f49d83d9aa00e6ce4fc8258c71cc7786aec968a
subpackages: subpackages:
- proto - proto
- name: github.com/howeyc/gopass - name: github.com/howeyc/gopass
version: b63a7d07e65df376d14e2d72907a93d4847dffe4 version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
- name: github.com/roasbeef/btcd - name: github.com/roasbeef/btcd
version: baea7691cc3c59480703fe1a3fb5595c838c963c version: baea7691cc3c59480703fe1a3fb5595c838c963c
subpackages: subpackages:
@ -79,9 +88,9 @@ imports:
- waddrmgr - waddrmgr
- wallet - wallet
- walletdb/bdb - walletdb/bdb
- walletdb
- internal/zero - internal/zero
- snacl - snacl
- walletdb
- wtxmgr - wtxmgr
- internal/prompt - internal/prompt
- wallet/txauthor - wallet/txauthor
@ -91,9 +100,9 @@ imports:
- wallet/internal/txsizes - wallet/internal/txsizes
- internal/legacy/rename - internal/legacy/rename
- name: github.com/urfave/cli - name: github.com/urfave/cli
version: 1efa31f08b9333f1bd4882d61f9d668a70cd902e version: a14d7d367bc02b1f57d88de97926727f2d936387
- name: golang.org/x/crypto - name: golang.org/x/crypto
version: 595bbbd7f5f308415a544d3c55743c91427c8d99 version: f160b6bf95857cd862817875dd958be022e587c4
subpackages: subpackages:
- hkdf - hkdf
- nacl/secretbox - nacl/secretbox
@ -104,7 +113,7 @@ imports:
- pbkdf2 - pbkdf2
- ssh/terminal - ssh/terminal
- name: golang.org/x/net - name: golang.org/x/net
version: 075e191f18186a8ff2becaf64478e30f4545cdad version: 1358eff22f0dd0c54fc521042cc607f6ff4b531a
subpackages: subpackages:
- context - context
- http2 - http2
@ -117,11 +126,11 @@ imports:
subpackages: subpackages:
- unix - unix
- name: google.golang.org/grpc - name: google.golang.org/grpc
version: 13edeeffdea7a41d5aad96c28deb4c7bd01a9397 version: 0032a855ba5c8a3c8e0d71c2deef354b70af1584
subpackages: subpackages:
- grpclog
- codes - codes
- credentials - credentials
- grpclog
- internal - internal
- metadata - metadata
- naming - naming

View File

@ -1,6 +1,7 @@
package: github.com/lightningnetwork/lnd package: github.com/lightningnetwork/lnd
import: import:
- package: github.com/BitfuryLightning/tools - package: github.com/BitfuryLightning/tools
version: ^0.0.2
subpackages: subpackages:
- routing - routing
- rt - rt

View File

@ -40,7 +40,7 @@ It has these top-level messages:
PendingChannelResponse PendingChannelResponse
WalletBalanceRequest WalletBalanceRequest
WalletBalanceResponse WalletBalanceResponse
RTChannel RoutingTableLink
ShowRoutingTableRequest ShowRoutingTableRequest
ShowRoutingTableResponse ShowRoutingTableResponse
*/ */
@ -799,18 +799,18 @@ func (m *WalletBalanceResponse) String() string { return proto.Compac
func (*WalletBalanceResponse) ProtoMessage() {} func (*WalletBalanceResponse) ProtoMessage() {}
func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} }
type RTChannel struct { type RoutingTableLink struct {
Id1 string `protobuf:"bytes,1,opt,name=id1" json:"id1,omitempty"` Id1 string `protobuf:"bytes,1,opt,name=id1" json:"id1,omitempty"`
Id2 string `protobuf:"bytes,2,opt,name=id2" json:"id2,omitempty"` Id2 string `protobuf:"bytes,2,opt,name=id2" json:"id2,omitempty"`
EdgeID string `protobuf:"bytes,3,opt,name=edgeID" json:"edgeID,omitempty"` Outpoint string `protobuf:"bytes,3,opt,name=outpoint" json:"outpoint,omitempty"`
Capacity float64 `protobuf:"fixed64,4,opt,name=capacity" json:"capacity,omitempty"` Capacity int64 `protobuf:"varint,4,opt,name=capacity" json:"capacity,omitempty"`
Weight float64 `protobuf:"fixed64,5,opt,name=weight" json:"weight,omitempty"` Weight float64 `protobuf:"fixed64,5,opt,name=weight" json:"weight,omitempty"`
} }
func (m *RTChannel) Reset() { *m = RTChannel{} } func (m *RoutingTableLink) Reset() { *m = RoutingTableLink{} }
func (m *RTChannel) String() string { return proto.CompactTextString(m) } func (m *RoutingTableLink) String() string { return proto.CompactTextString(m) }
func (*RTChannel) ProtoMessage() {} func (*RoutingTableLink) ProtoMessage() {}
func (*RTChannel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } func (*RoutingTableLink) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} }
type ShowRoutingTableRequest struct { type ShowRoutingTableRequest struct {
} }
@ -821,7 +821,7 @@ func (*ShowRoutingTableRequest) ProtoMessage() {}
func (*ShowRoutingTableRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } func (*ShowRoutingTableRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} }
type ShowRoutingTableResponse struct { type ShowRoutingTableResponse struct {
Channels []*RTChannel `protobuf:"bytes,1,rep,name=channels" json:"channels,omitempty"` Channels []*RoutingTableLink `protobuf:"bytes,1,rep,name=channels" json:"channels,omitempty"`
} }
func (m *ShowRoutingTableResponse) Reset() { *m = ShowRoutingTableResponse{} } func (m *ShowRoutingTableResponse) Reset() { *m = ShowRoutingTableResponse{} }
@ -829,7 +829,7 @@ func (m *ShowRoutingTableResponse) String() string { return proto.Com
func (*ShowRoutingTableResponse) ProtoMessage() {} func (*ShowRoutingTableResponse) ProtoMessage() {}
func (*ShowRoutingTableResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } func (*ShowRoutingTableResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} }
func (m *ShowRoutingTableResponse) GetChannels() []*RTChannel { func (m *ShowRoutingTableResponse) GetChannels() []*RoutingTableLink {
if m != nil { if m != nil {
return m.Channels return m.Channels
} }
@ -869,7 +869,7 @@ func init() {
proto.RegisterType((*PendingChannelResponse_PendingChannel)(nil), "lnrpc.PendingChannelResponse.PendingChannel") proto.RegisterType((*PendingChannelResponse_PendingChannel)(nil), "lnrpc.PendingChannelResponse.PendingChannel")
proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest") proto.RegisterType((*WalletBalanceRequest)(nil), "lnrpc.WalletBalanceRequest")
proto.RegisterType((*WalletBalanceResponse)(nil), "lnrpc.WalletBalanceResponse") proto.RegisterType((*WalletBalanceResponse)(nil), "lnrpc.WalletBalanceResponse")
proto.RegisterType((*RTChannel)(nil), "lnrpc.RTChannel") proto.RegisterType((*RoutingTableLink)(nil), "lnrpc.RoutingTableLink")
proto.RegisterType((*ShowRoutingTableRequest)(nil), "lnrpc.ShowRoutingTableRequest") proto.RegisterType((*ShowRoutingTableRequest)(nil), "lnrpc.ShowRoutingTableRequest")
proto.RegisterType((*ShowRoutingTableResponse)(nil), "lnrpc.ShowRoutingTableResponse") proto.RegisterType((*ShowRoutingTableResponse)(nil), "lnrpc.ShowRoutingTableResponse")
proto.RegisterEnum("lnrpc.ChannelStatus", ChannelStatus_name, ChannelStatus_value) proto.RegisterEnum("lnrpc.ChannelStatus", ChannelStatus_name, ChannelStatus_value)
@ -1401,102 +1401,101 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 1539 bytes of a gzipped FileDescriptorProto // 1535 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x57, 0x5d, 0x6f, 0x1a, 0x57, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x57, 0x5d, 0x6f, 0x13, 0x57,
0x13, 0xce, 0x1a, 0x30, 0x30, 0x80, 0x0d, 0xc7, 0x5f, 0x98, 0x24, 0xef, 0x9b, 0xf7, 0x28, 0x89, 0x13, 0xc6, 0xdf, 0xf6, 0xd8, 0x4e, 0xec, 0x93, 0x2f, 0xc7, 0xc0, 0xfb, 0xf2, 0xae, 0x00, 0xe5,
0xac, 0xc8, 0x71, 0x12, 0xf2, 0x5e, 0x44, 0x89, 0x9a, 0xca, 0xc1, 0x6e, 0xec, 0xc6, 0xb5, 0xdd, 0x45, 0x21, 0x80, 0xe9, 0x05, 0x02, 0x95, 0x2a, 0x98, 0x94, 0x50, 0xd2, 0x24, 0xc5, 0x41, 0xa8,
0x40, 0x14, 0xf5, 0x6a, 0xb5, 0xec, 0x1e, 0x9b, 0x55, 0x96, 0x5d, 0xca, 0x1e, 0xe2, 0xd0, 0x1f, 0x57, 0xdb, 0xf5, 0xfa, 0x04, 0xaf, 0x58, 0xef, 0xba, 0x3e, 0xc7, 0x04, 0xf7, 0x07, 0xf4, 0xb6,
0xd0, 0xdb, 0xde, 0xf6, 0x47, 0x54, 0x55, 0xff, 0x43, 0xd5, 0xab, 0x4a, 0xfd, 0x4d, 0x9d, 0xf3, 0xb7, 0xfd, 0x11, 0x55, 0xd5, 0xff, 0x50, 0xf5, 0xaa, 0x52, 0x7f, 0x53, 0xe7, 0x7c, 0xd9, 0xfb,
0xb1, 0xb0, 0x1f, 0x38, 0x52, 0x2f, 0x7a, 0x85, 0x76, 0xce, 0x9c, 0xf9, 0x78, 0x66, 0xe6, 0x39, 0xe1, 0x20, 0xf5, 0xa2, 0x57, 0xd6, 0xce, 0x99, 0x33, 0x1f, 0xcf, 0xcc, 0x79, 0x66, 0x0c, 0x95,
0x03, 0x94, 0xc7, 0x23, 0x7b, 0x6f, 0x34, 0x0e, 0x78, 0x40, 0x0a, 0x9e, 0x8f, 0x1f, 0xf4, 0x5b, 0xc9, 0xd8, 0xdd, 0x1b, 0x4f, 0x42, 0x1e, 0x92, 0x82, 0x1f, 0xe0, 0x87, 0xf5, 0x0d, 0x54, 0x7b,
0xa8, 0x74, 0x99, 0xef, 0xbc, 0x65, 0xdf, 0x4f, 0x58, 0xc8, 0x49, 0x15, 0xf2, 0x0e, 0xfe, 0x36, 0x34, 0x18, 0xbc, 0xa6, 0xdf, 0x4f, 0x29, 0xe3, 0xa4, 0x06, 0xf9, 0x01, 0xfe, 0xb6, 0x32, 0x37,
0x8d, 0x3b, 0xc6, 0x4e, 0x95, 0x54, 0x20, 0x67, 0x0d, 0x79, 0x73, 0x09, 0x3f, 0x72, 0x64, 0x1d, 0x32, 0x3b, 0x35, 0x52, 0x85, 0x9c, 0x33, 0xe2, 0xad, 0x2c, 0x7e, 0xe4, 0xc8, 0x3a, 0xd4, 0xc6,
0xaa, 0x23, 0x6b, 0x3a, 0x64, 0x3e, 0x37, 0x07, 0x56, 0x38, 0x68, 0xe6, 0xa4, 0x4a, 0x03, 0xca, 0xce, 0x6c, 0x44, 0x03, 0x6e, 0x0f, 0x1d, 0x36, 0x6c, 0xe5, 0xa4, 0x4a, 0x13, 0x2a, 0xe7, 0x0e,
0x17, 0x56, 0xc8, 0xcd, 0x10, 0x8d, 0x34, 0xf3, 0x28, 0x2a, 0xd1, 0x15, 0xa8, 0x2a, 0x93, 0xe1, 0xe3, 0x36, 0x43, 0x23, 0xad, 0x3c, 0x8a, 0xca, 0xd6, 0x0a, 0xd4, 0x94, 0x49, 0x36, 0x0e, 0x03,
0x28, 0xf0, 0x43, 0x46, 0x9f, 0x43, 0xb5, 0x33, 0xb0, 0x7c, 0x9f, 0x79, 0xe7, 0x81, 0xeb, 0x73, 0x46, 0xad, 0xc7, 0x50, 0xeb, 0x0e, 0x9d, 0x20, 0xa0, 0xfe, 0x69, 0xe8, 0x05, 0x5c, 0x18, 0x3a,
0x61, 0xe8, 0x62, 0xe2, 0x3b, 0xae, 0x7f, 0x69, 0xf2, 0x4f, 0xae, 0xa3, 0x7d, 0xa1, 0x34, 0x98, 0x9f, 0x06, 0x03, 0x2f, 0x78, 0x67, 0xf3, 0x8f, 0xde, 0x40, 0xfb, 0x42, 0x69, 0x38, 0xe5, 0xe3,
0xf0, 0xd1, 0x84, 0x9b, 0xae, 0xef, 0xb0, 0x4f, 0xd2, 0x69, 0x8d, 0xfe, 0x1f, 0xea, 0x27, 0xee, 0x29, 0xb7, 0xbd, 0x60, 0x40, 0x3f, 0x4a, 0xa7, 0x75, 0xeb, 0x33, 0x68, 0x1c, 0x79, 0xef, 0x86,
0xe5, 0x80, 0xfb, 0xa8, 0xbd, 0xef, 0x38, 0x63, 0x16, 0x86, 0x84, 0x00, 0x8c, 0x26, 0xfd, 0x37, 0x3c, 0x40, 0xed, 0xfd, 0xc1, 0x60, 0x42, 0x19, 0x23, 0x04, 0x60, 0x3c, 0xed, 0xbf, 0xa2, 0xb3,
0x6c, 0x7a, 0x24, 0xc2, 0x10, 0xb7, 0xcb, 0x22, 0xee, 0x41, 0x10, 0xaa, 0x50, 0xcb, 0xf4, 0x47, 0x43, 0x11, 0x86, 0xb8, 0x5d, 0x11, 0x71, 0x0f, 0x43, 0xa6, 0x42, 0xad, 0x58, 0x3f, 0x66, 0x60,
0x03, 0x56, 0x45, 0x08, 0xdf, 0x58, 0xfe, 0x34, 0xca, 0xec, 0x25, 0x54, 0x85, 0x81, 0x5e, 0xb0, 0x55, 0x84, 0xf0, 0xb5, 0x13, 0xcc, 0x4c, 0x66, 0x4f, 0xa1, 0x26, 0x0c, 0x9c, 0x85, 0xfb, 0xa3,
0x3f, 0x0c, 0x26, 0xbe, 0xc8, 0x30, 0xb7, 0x53, 0x69, 0xef, 0xec, 0x49, 0x18, 0xf6, 0x52, 0xda, 0x70, 0x1a, 0x88, 0x0c, 0x73, 0x3b, 0xd5, 0xce, 0xce, 0x9e, 0x84, 0x61, 0x2f, 0xa1, 0xbd, 0x17,
0x7b, 0x71, 0xd5, 0x43, 0x9f, 0x8f, 0xa7, 0xad, 0xa7, 0xd0, 0xc8, 0x08, 0x05, 0x40, 0x1f, 0xd8, 0x55, 0x3d, 0x08, 0xf8, 0x64, 0xd6, 0x7e, 0x08, 0xcd, 0x94, 0x50, 0x00, 0xf4, 0x9e, 0xce, 0x74,
0x54, 0xc7, 0x50, 0x83, 0xc2, 0x47, 0xcb, 0x9b, 0x30, 0x85, 0xd7, 0xf3, 0xa5, 0x67, 0x06, 0xbd, 0x0c, 0x75, 0x28, 0x7c, 0x70, 0xfc, 0x29, 0x55, 0x78, 0x3d, 0xce, 0x3e, 0xca, 0x58, 0x37, 0xa0,
0x03, 0xf5, 0xb9, 0x65, 0x05, 0x87, 0x08, 0x75, 0x96, 0x76, 0x99, 0x3e, 0x56, 0x1a, 0x1d, 0x44, 0xb1, 0xb0, 0xac, 0xe0, 0x10, 0xa1, 0xce, 0xd3, 0xae, 0x58, 0xf7, 0x95, 0x46, 0x17, 0x91, 0x61,
0x26, 0x8c, 0x15, 0xc1, 0x42, 0x57, 0xda, 0xec, 0x0a, 0x2c, 0x5b, 0x2a, 0x64, 0x69, 0x97, 0xfe, 0x91, 0x22, 0x38, 0xe8, 0x4a, 0x9b, 0x5d, 0x81, 0xa2, 0xa3, 0x42, 0x96, 0x76, 0xad, 0xff, 0x41,
0x0f, 0x1a, 0xb1, 0x1b, 0x0b, 0x8d, 0xfe, 0x6c, 0x40, 0xe3, 0x94, 0x5d, 0x69, 0xc0, 0x22, 0xb3, 0x33, 0x72, 0x63, 0xa9, 0xd1, 0x9f, 0x33, 0xd0, 0x3c, 0xa6, 0x17, 0x1a, 0x30, 0x63, 0xb6, 0x83,
0x6d, 0xd4, 0x99, 0x8e, 0x98, 0xd4, 0x59, 0x69, 0xdf, 0xd5, 0x99, 0x67, 0xf4, 0xf6, 0xf4, 0x67, 0x3a, 0xb3, 0x31, 0x95, 0x3a, 0x2b, 0x9d, 0x9b, 0x3a, 0xf3, 0x94, 0xde, 0x9e, 0xfe, 0x3c, 0x43,
0x0f, 0x75, 0xe9, 0x19, 0x54, 0x62, 0x9f, 0x64, 0x0b, 0xd6, 0xde, 0x1f, 0xf7, 0x4e, 0x0f, 0xbb, 0x5d, 0xeb, 0x04, 0xaa, 0x91, 0x4f, 0xb2, 0x05, 0x6b, 0x6f, 0x5f, 0x9e, 0x1d, 0x1f, 0xf4, 0x7a,
0x5d, 0xf3, 0xfc, 0xdd, 0xab, 0x37, 0x87, 0xdf, 0x99, 0x47, 0xfb, 0xdd, 0xa3, 0xfa, 0x0d, 0xb2, 0xf6, 0xe9, 0x9b, 0x67, 0xaf, 0x0e, 0xbe, 0xb5, 0x0f, 0xf7, 0x7b, 0x87, 0x8d, 0x2b, 0x64, 0x13,
0x09, 0x04, 0xa5, 0xbd, 0xc3, 0x83, 0x84, 0xdc, 0x20, 0xab, 0x50, 0x89, 0x0b, 0x96, 0xe8, 0x3d, 0x08, 0x4a, 0xcf, 0x0e, 0x9e, 0xc7, 0xe4, 0x19, 0xb2, 0x0a, 0xd5, 0xa8, 0x20, 0x6b, 0xdd, 0x42,
0x54, 0x8c, 0x79, 0xd4, 0xe1, 0xaf, 0x42, 0xd1, 0x52, 0x22, 0x9d, 0xc1, 0x0b, 0x20, 0x9d, 0x00, 0xc5, 0x88, 0x47, 0x1d, 0xfe, 0x2a, 0x94, 0x1c, 0x25, 0xd2, 0x19, 0x3c, 0x01, 0xd2, 0x0d, 0xb1,
0x5b, 0xc6, 0xe6, 0xe7, 0x8c, 0x8d, 0xa3, 0x0c, 0xee, 0xc5, 0x80, 0xa9, 0xb4, 0xb7, 0x74, 0x06, 0x65, 0x5c, 0x7e, 0x4a, 0xe9, 0xc4, 0x64, 0x70, 0x2b, 0x02, 0x4c, 0xb5, 0xb3, 0xa5, 0x33, 0x48,
0xe9, 0x06, 0xa1, 0xf7, 0x61, 0x2d, 0x71, 0x79, 0xee, 0x64, 0x84, 0xdf, 0xa6, 0x86, 0xa9, 0x40, 0x36, 0x88, 0x75, 0x1b, 0xd6, 0x62, 0x97, 0x17, 0x4e, 0xc6, 0xf8, 0x6d, 0x6b, 0x98, 0x0a, 0xd6,
0x0f, 0x20, 0x7f, 0xd4, 0x3b, 0xe9, 0x60, 0x3f, 0x2d, 0x69, 0x59, 0x2e, 0x8d, 0xb6, 0xe8, 0x6f, 0x73, 0xc8, 0x1f, 0x9e, 0x1d, 0x75, 0xb1, 0x9f, 0xb2, 0x5a, 0x96, 0x4b, 0xa2, 0x2d, 0xfa, 0x5b,
0xd1, 0xed, 0xa6, 0x17, 0xd8, 0x1f, 0x74, 0xcb, 0x63, 0x9d, 0x79, 0x60, 0x4e, 0x42, 0xdd, 0xee, 0x74, 0xbb, 0xed, 0x87, 0xee, 0x7b, 0xdd, 0xf2, 0x58, 0x67, 0x1e, 0xda, 0x53, 0xa6, 0xdb, 0xfd,
0x7f, 0x19, 0x50, 0xdb, 0xb7, 0xb9, 0xfb, 0x91, 0xe9, 0x2e, 0x17, 0x77, 0xc6, 0x6c, 0x18, 0x70, 0xaf, 0x0c, 0xd4, 0xf7, 0x5d, 0xee, 0x7d, 0xa0, 0xba, 0xcb, 0xc5, 0x9d, 0x09, 0x1d, 0x85, 0x9c,
0x16, 0xb9, 0x2a, 0x93, 0x0d, 0xa8, 0xd9, 0xea, 0xd4, 0x1c, 0x89, 0x21, 0x50, 0x8d, 0x4a, 0xea, 0x1a, 0x57, 0x15, 0xb2, 0x01, 0x75, 0x57, 0x9d, 0xda, 0x63, 0xf1, 0x08, 0x54, 0xa3, 0x92, 0x06,
0x50, 0xb2, 0xad, 0x91, 0x65, 0xbb, 0x7c, 0x2a, 0x8d, 0xe7, 0x84, 0x22, 0xba, 0xb2, 0x3c, 0xb3, 0x94, 0x5d, 0x67, 0xec, 0xb8, 0x1e, 0x9f, 0x49, 0xe3, 0x39, 0xa1, 0x88, 0xae, 0x1c, 0xdf, 0xee,
0x6f, 0x79, 0x96, 0x6f, 0x33, 0xe9, 0x24, 0x87, 0xf8, 0xae, 0x68, 0x93, 0x91, 0xbc, 0x20, 0xe5, 0x3b, 0xbe, 0x13, 0xb8, 0x54, 0x3a, 0xc9, 0x21, 0xbe, 0x2b, 0xda, 0xa4, 0x91, 0x17, 0xa4, 0x7c,
0xdb, 0xd0, 0x98, 0x60, 0x6e, 0x9c, 0x7b, 0xcc, 0x31, 0xfb, 0x4c, 0x1d, 0x2d, 0xcb, 0x23, 0x0a, 0x1b, 0x9a, 0x53, 0xcc, 0x8d, 0x73, 0x9f, 0x0e, 0xec, 0x3e, 0x55, 0x47, 0x45, 0x79, 0x64, 0x41,
0xb5, 0x11, 0x53, 0x63, 0x36, 0xe0, 0x9e, 0x1d, 0x36, 0x8b, 0xb2, 0xe3, 0x2b, 0x1a, 0x35, 0x99, 0x7d, 0x4c, 0xd5, 0x33, 0x1b, 0x72, 0xdf, 0x65, 0xad, 0x92, 0xec, 0xf8, 0xaa, 0x46, 0x4d, 0x66,
0xf9, 0x1a, 0x54, 0xfc, 0xc9, 0xd0, 0x9c, 0x8c, 0x1c, 0x8b, 0xb3, 0xb0, 0x59, 0xc2, 0x8b, 0x79, 0xbe, 0x06, 0xd5, 0x60, 0x3a, 0xb2, 0xa7, 0xe3, 0x81, 0xc3, 0x29, 0x6b, 0x95, 0xf1, 0x62, 0xde,
0xfa, 0xbb, 0x01, 0x79, 0x01, 0x9c, 0x18, 0x49, 0x2f, 0xc2, 0x76, 0x9e, 0x4a, 0x0c, 0x46, 0x91, 0xfa, 0x3d, 0x03, 0x79, 0x01, 0x9c, 0x78, 0x92, 0xbe, 0xc1, 0x76, 0x91, 0x4a, 0x04, 0x46, 0x91,
0x44, 0x21, 0x5e, 0xbc, 0x9c, 0xd4, 0x40, 0x40, 0xfb, 0x53, 0xb4, 0x27, 0x48, 0x81, 0xcb, 0x04, 0x44, 0x21, 0x5a, 0xbc, 0x9c, 0xd4, 0x40, 0x40, 0xfb, 0x33, 0xb4, 0x27, 0x48, 0x81, 0xcb, 0x04,
0xf2, 0x73, 0xd9, 0x98, 0xd9, 0x1f, 0x65, 0xf0, 0x79, 0x91, 0x7d, 0x68, 0x71, 0xa5, 0xa5, 0x62, 0xf2, 0x0b, 0xd9, 0x84, 0xba, 0x1f, 0x64, 0xf0, 0x79, 0x91, 0x3d, 0x73, 0xb8, 0xd2, 0x52, 0x31,
0xd6, 0x12, 0xa9, 0x53, 0x94, 0x12, 0x34, 0xee, 0xfa, 0x7d, 0x2c, 0x88, 0x23, 0xa3, 0x2b, 0x91, 0x6b, 0x89, 0xd4, 0x29, 0x49, 0x09, 0x1a, 0xf7, 0x82, 0x3e, 0x16, 0x64, 0x20, 0xa3, 0x2b, 0x93,
0xfb, 0x08, 0x99, 0x42, 0x32, 0x6c, 0x96, 0x65, 0x46, 0xeb, 0x3a, 0xa3, 0x44, 0x11, 0x28, 0x11, 0xdb, 0x08, 0x99, 0x42, 0x92, 0xb5, 0x2a, 0x32, 0xa3, 0x75, 0x9d, 0x51, 0xac, 0x08, 0x16, 0x11,
0xcc, 0x11, 0xca, 0x0e, 0x88, 0x3a, 0x9b, 0x3e, 0x82, 0x46, 0x4c, 0xa6, 0xdb, 0xa2, 0x05, 0x05, 0xcc, 0xc1, 0x64, 0x07, 0x98, 0xce, 0xb6, 0xee, 0x41, 0x33, 0x22, 0xd3, 0x6d, 0xd1, 0x86, 0x82,
0x91, 0x4f, 0xa8, 0x19, 0x21, 0xc2, 0x47, 0x28, 0xd1, 0x3a, 0xac, 0xbc, 0x66, 0xfc, 0xd8, 0xbf, 0xc8, 0x87, 0x69, 0x46, 0x30, 0xf8, 0x08, 0x25, 0xab, 0x01, 0x2b, 0x2f, 0x28, 0x7f, 0x19, 0x9c,
0x08, 0x22, 0x13, 0x3f, 0x21, 0xb5, 0xcc, 0x44, 0xda, 0xc2, 0x62, 0x9c, 0x9a, 0x50, 0x77, 0x1d, 0x87, 0xc6, 0xc4, 0x4f, 0x48, 0x2d, 0x73, 0x91, 0xb6, 0xb0, 0x1c, 0xa7, 0x16, 0x34, 0xbc, 0x01,
0x4c, 0x0d, 0x6b, 0x6b, 0x46, 0xf8, 0xa8, 0xaa, 0xdf, 0x82, 0x75, 0x81, 0x7a, 0x54, 0x9d, 0x59, 0xa6, 0x86, 0xb5, 0xb5, 0x0d, 0x3e, 0xaa, 0xea, 0xd7, 0x60, 0x5d, 0xa0, 0x6e, 0xaa, 0x33, 0x4f,
0x3a, 0x02, 0xbd, 0x1a, 0xb9, 0x09, 0x6b, 0xe2, 0xd4, 0x92, 0xd9, 0xcc, 0x0f, 0xf3, 0xf2, 0x10, 0x47, 0xa0, 0x57, 0x27, 0x57, 0x61, 0x4d, 0x9c, 0x3a, 0x32, 0x9b, 0xc5, 0x61, 0x5e, 0x1e, 0x62,
0x5b, 0x4b, 0x5d, 0x15, 0x01, 0x17, 0x24, 0x45, 0xbe, 0x93, 0xa3, 0x72, 0xe1, 0x8e, 0x87, 0x16, 0x6b, 0xa9, 0xab, 0x22, 0xe0, 0x82, 0xa4, 0xc8, 0x37, 0xf2, 0xa9, 0x9c, 0x7b, 0x93, 0x91, 0xc3,
0x77, 0x03, 0xff, 0x9d, 0xac, 0xa5, 0x50, 0xec, 0x8b, 0x9e, 0x35, 0xc3, 0x81, 0x35, 0x67, 0x58, 0xbd, 0x30, 0x78, 0x23, 0x6b, 0x29, 0x14, 0xfb, 0xa2, 0x67, 0x6d, 0x36, 0x74, 0x16, 0x0c, 0xab,
0x25, 0x1a, 0x30, 0x11, 0xad, 0xae, 0x1e, 0x76, 0x96, 0xb0, 0x68, 0xa3, 0x89, 0xd0, 0xf4, 0xd8, 0x44, 0x43, 0x2a, 0xa2, 0xd5, 0xd5, 0xc3, 0xce, 0x12, 0x16, 0x5d, 0x34, 0xc1, 0x6c, 0x9f, 0x9e,
0x05, 0x57, 0x61, 0xd0, 0x2f, 0xa1, 0xa1, 0xa1, 0x3c, 0xc3, 0x40, 0xb5, 0xd5, 0x07, 0xe9, 0x36, 0x73, 0x15, 0x86, 0xf5, 0x05, 0x34, 0x35, 0x94, 0x27, 0x18, 0xa8, 0xb6, 0x7a, 0x27, 0xd9, 0xc6,
0x56, 0x93, 0xb8, 0xa6, 0x31, 0x8b, 0xd3, 0xbc, 0x1c, 0x61, 0xf5, 0xdd, 0xf1, 0x82, 0x90, 0x69, 0xea, 0x25, 0xae, 0x69, 0xcc, 0xa2, 0x34, 0x2f, 0x9f, 0xb0, 0xfa, 0xee, 0xfa, 0x21, 0xa3, 0xda,
0x0b, 0x18, 0x84, 0x8d, 0x9f, 0x29, 0xf2, 0xc7, 0x2a, 0x87, 0x13, 0xdb, 0x8e, 0x20, 0x2a, 0xd1, 0x02, 0x06, 0xe1, 0xe2, 0x67, 0x82, 0xfc, 0xb1, 0xca, 0x6c, 0xea, 0xba, 0x06, 0xa2, 0xb2, 0x35,
0x11, 0x8e, 0xb0, 0xb8, 0xa5, 0x2d, 0x44, 0x04, 0xf0, 0x0f, 0xfc, 0x8b, 0x8e, 0xe3, 0xee, 0x90, 0xc6, 0x27, 0x2c, 0x6e, 0x69, 0x0b, 0x86, 0x00, 0xfe, 0x81, 0x7f, 0xd1, 0x71, 0xdc, 0x1b, 0x51,
0x99, 0x9e, 0x3b, 0x74, 0xa3, 0x69, 0xc6, 0x71, 0xb1, 0x3c, 0x2f, 0xb8, 0x32, 0x2f, 0x82, 0xb1, 0xdb, 0xf7, 0x46, 0x9e, 0x79, 0xcd, 0xf8, 0x5c, 0x1c, 0xdf, 0x0f, 0x2f, 0xec, 0xf3, 0x70, 0xe2,
0x8d, 0xe0, 0x0a, 0x17, 0x32, 0xdf, 0x12, 0xfd, 0x0d, 0x39, 0x53, 0xba, 0xec, 0x72, 0x8b, 0x4f, 0x22, 0xb8, 0xc2, 0x85, 0xcc, 0xb7, 0x6c, 0xfd, 0x86, 0x9c, 0x29, 0x5d, 0xf6, 0xb8, 0xc3, 0xa7,
0x42, 0x1d, 0xee, 0x43, 0x74, 0x28, 0x84, 0x51, 0xb1, 0xb4, 0xc3, 0xf5, 0x59, 0x93, 0x48, 0xa9, 0x4c, 0x87, 0x7b, 0x17, 0x1d, 0x0a, 0xa1, 0x29, 0x96, 0x76, 0xb8, 0x3e, 0x6f, 0x12, 0x29, 0x55,
0x52, 0x3e, 0xba, 0x41, 0x9e, 0x60, 0x76, 0xb1, 0x5a, 0x48, 0xaf, 0x95, 0xf6, 0x76, 0x14, 0x5e, 0xca, 0x87, 0x57, 0xc8, 0x03, 0xcc, 0x2e, 0x52, 0x0b, 0xe9, 0xb5, 0xda, 0xd9, 0x36, 0xe1, 0xa5,
0xa6, 0x4c, 0x78, 0xe5, 0x11, 0x80, 0x48, 0x29, 0x16, 0x4b, 0xec, 0x42, 0x06, 0xbf, 0xa3, 0x1b, 0xca, 0x84, 0x57, 0xee, 0x01, 0x88, 0x94, 0x22, 0xb1, 0x44, 0x2e, 0xa4, 0xf0, 0x3b, 0xbc, 0xf2,
0xaf, 0x4a, 0xb0, 0xac, 0xe6, 0x95, 0xde, 0x86, 0x5a, 0x22, 0x80, 0xc4, 0x2b, 0x50, 0x15, 0x73, 0xac, 0x0c, 0x45, 0xf5, 0x5e, 0xad, 0xeb, 0x50, 0x8f, 0x05, 0x10, 0x9b, 0x02, 0x35, 0xf1, 0x8e,
0x4c, 0x44, 0xed, 0x52, 0x18, 0x62, 0xc1, 0xb9, 0x35, 0xbe, 0x64, 0xdc, 0x4c, 0xb0, 0x21, 0xd9, 0x89, 0xa8, 0x5d, 0x02, 0x43, 0x2c, 0x38, 0x77, 0x26, 0xef, 0x28, 0xb7, 0x63, 0x6c, 0x48, 0x76,
0x85, 0x8a, 0x96, 0xfb, 0x81, 0xc3, 0x74, 0xe8, 0xd7, 0x71, 0xac, 0xe8, 0x61, 0xc5, 0x53, 0xd1, 0xa1, 0xaa, 0xe5, 0x41, 0x38, 0xa0, 0x3a, 0xf4, 0xcb, 0x38, 0x56, 0xf4, 0xb0, 0xe2, 0x29, 0x33,
0x53, 0xae, 0x59, 0x53, 0xb1, 0xd8, 0x6d, 0xd8, 0xd0, 0x74, 0x95, 0x3a, 0x56, 0x6c, 0xb6, 0x05, 0xca, 0x35, 0x6b, 0x2a, 0x16, 0xbb, 0x0e, 0x1b, 0x9a, 0xae, 0x12, 0xc7, 0x8a, 0xcd, 0xb6, 0x60,
0xab, 0x76, 0x30, 0x1c, 0xba, 0x61, 0x88, 0x48, 0x98, 0xa1, 0xfb, 0x43, 0x44, 0x67, 0xba, 0xbd, 0xd5, 0x0d, 0x47, 0x23, 0x8f, 0x31, 0x44, 0xc2, 0x66, 0xde, 0x0f, 0x86, 0xce, 0x74, 0x7b, 0xcb,
0x65, 0x33, 0x4a, 0x4a, 0xa8, 0xd1, 0x5f, 0x0c, 0xa8, 0x8b, 0x2c, 0x12, 0x65, 0xd9, 0x45, 0x9c, 0x66, 0x94, 0x94, 0x50, 0xb7, 0x7e, 0xc9, 0x40, 0x43, 0x64, 0x11, 0x2b, 0xcb, 0x2e, 0xe2, 0x2c,
0x05, 0x68, 0xff, 0x5a, 0x55, 0x1e, 0x42, 0x59, 0x3a, 0x08, 0xd0, 0x83, 0x2e, 0x4a, 0x33, 0x59, 0x40, 0xfb, 0xd7, 0xaa, 0x72, 0x17, 0x2a, 0xd2, 0x41, 0x88, 0x1e, 0x74, 0x51, 0x5a, 0xf1, 0xa2,
0x94, 0xf9, 0x54, 0x24, 0x6a, 0xf2, 0x05, 0x6c, 0x68, 0xf7, 0x29, 0xd8, 0xef, 0xc2, 0x72, 0x28, 0x2c, 0x5e, 0x45, 0xac, 0x26, 0x9f, 0xc3, 0x86, 0x76, 0x9f, 0x80, 0xfd, 0x26, 0x14, 0x99, 0x4c,
0x53, 0xd0, 0xef, 0xef, 0x7a, 0xd2, 0x9c, 0x4a, 0x8f, 0xfe, 0xba, 0x04, 0x9b, 0xe9, 0xfb, 0x9a, 0x41, 0xcf, 0xdf, 0xf5, 0xb8, 0x39, 0x95, 0x9e, 0xf5, 0x6b, 0x16, 0x36, 0x93, 0xf7, 0x35, 0xcb,
0x65, 0xbe, 0x82, 0x7a, 0x86, 0x31, 0x14, 0x65, 0xed, 0x26, 0xf3, 0x4e, 0x5d, 0x4c, 0x89, 0x5b, 0x7c, 0x09, 0x8d, 0x14, 0x63, 0x28, 0xca, 0xda, 0x8d, 0xe7, 0x9d, 0xb8, 0x98, 0x10, 0xb7, 0xff,
0x7f, 0x1a, 0xb0, 0x92, 0x14, 0x65, 0x5e, 0xc6, 0x0c, 0xa3, 0x2d, 0x2d, 0x7e, 0xc4, 0x72, 0x99, 0xcc, 0xc0, 0x4a, 0x5c, 0x94, 0x9a, 0x8c, 0x29, 0x46, 0xcb, 0x2e, 0x1f, 0x62, 0xb9, 0xd4, 0x10,
0x47, 0x2c, 0xbf, 0xf8, 0x11, 0x2b, 0x5c, 0xf3, 0x88, 0x2d, 0x47, 0x9b, 0x65, 0x82, 0x13, 0x8a, 0xcb, 0x2f, 0x1f, 0x62, 0x85, 0x4b, 0x86, 0x58, 0xd1, 0x6c, 0x96, 0x31, 0x4e, 0x28, 0x49, 0xb3,
0xd2, 0xec, 0x1c, 0xb0, 0xd2, 0x67, 0x00, 0xdb, 0x85, 0xf5, 0xf7, 0x38, 0xd2, 0x8c, 0xbf, 0x52, 0x0b, 0xc0, 0xca, 0x9f, 0x00, 0x6c, 0x17, 0xd6, 0xdf, 0xe2, 0x93, 0xa6, 0xfc, 0x99, 0x32, 0x69,
0x26, 0x23, 0xb8, 0xd1, 0xe6, 0x95, 0xcb, 0x7d, 0x6c, 0x55, 0x33, 0xf0, 0x3d, 0xb5, 0xa2, 0x95, 0xe0, 0x46, 0x9b, 0x17, 0x1e, 0x0f, 0xb0, 0x55, 0xed, 0x30, 0xf0, 0xd5, 0x8a, 0x56, 0xb6, 0x76,
0xe8, 0x0e, 0x6c, 0xa4, 0xb4, 0xe7, 0xbb, 0x41, 0x14, 0x93, 0xd0, 0x34, 0xe8, 0x7b, 0x28, 0xbf, 0x60, 0x23, 0xa1, 0xbd, 0xd8, 0x0d, 0x4c, 0x4c, 0x42, 0x33, 0x63, 0x7d, 0x07, 0x8d, 0xd7, 0xb8,
0xed, 0x45, 0xf8, 0xe0, 0x9a, 0xe7, 0x3a, 0x4f, 0x34, 0xaf, 0xcb, 0x8f, 0xb6, 0x86, 0x04, 0xd7, 0x8f, 0x62, 0x4c, 0x67, 0x4e, 0xdf, 0xa7, 0x47, 0x5e, 0xf0, 0x5e, 0x6c, 0x7b, 0xde, 0xe0, 0x81,
0x05, 0xe6, 0x5c, 0xb2, 0xe3, 0x83, 0x6b, 0xb0, 0x30, 0x84, 0xc6, 0x95, 0xe2, 0xdb, 0x82, 0x34, 0xa6, 0x77, 0xf9, 0xd1, 0x59, 0xcc, 0x71, 0xb1, 0xbc, 0x7e, 0x12, 0x14, 0xdc, 0x2c, 0x2e, 0x14,
0xbc, 0x0d, 0x5b, 0xdd, 0x41, 0x70, 0xf5, 0x16, 0x97, 0x5d, 0x4c, 0xb8, 0x67, 0xf5, 0xbd, 0x28, 0xf1, 0x16, 0xa4, 0x87, 0x6d, 0xd8, 0xea, 0x0d, 0xc3, 0x8b, 0xa8, 0x17, 0x33, 0x64, 0x0e, 0xa0,
0x66, 0xfa, 0x12, 0x9a, 0xd9, 0x23, 0x1d, 0x20, 0x8d, 0x3d, 0x7b, 0xaa, 0xea, 0x75, 0x8d, 0xc7, 0x95, 0x3e, 0xd2, 0x91, 0xfe, 0x3f, 0x32, 0xff, 0x54, 0xf9, 0xcd, 0x1b, 0x4d, 0xc6, 0x7b, 0xa7,
0x2c, 0xcc, 0x07, 0x6d, 0xa8, 0x25, 0xc0, 0x21, 0x45, 0xc8, 0xed, 0x9f, 0x9c, 0xe0, 0x7a, 0x56, 0x03, 0xf5, 0x18, 0x58, 0xa4, 0x04, 0xb9, 0xfd, 0xa3, 0x23, 0x5c, 0xd7, 0xaa, 0x50, 0x3a, 0x39,
0x81, 0xe2, 0xd9, 0xf9, 0xe1, 0xe9, 0xf1, 0xe9, 0x6b, 0xdc, 0xc9, 0xf0, 0xa3, 0x73, 0x72, 0xd6, 0x3d, 0x38, 0x7e, 0x79, 0xfc, 0x02, 0x77, 0x34, 0xfc, 0xe8, 0x1e, 0x9d, 0xf4, 0xc4, 0x47, 0xb6,
0x15, 0x1f, 0x4b, 0xed, 0x3f, 0x96, 0xa1, 0x3c, 0x1b, 0x6e, 0xf2, 0x35, 0xd4, 0x12, 0xf8, 0x90, 0xf3, 0x47, 0x11, 0x2a, 0xf3, 0xc7, 0x4e, 0xbe, 0x82, 0x7a, 0x0c, 0x2f, 0x72, 0x55, 0xfb, 0x5a,
0x9b, 0xda, 0xc9, 0x22, 0x8c, 0x5b, 0xb7, 0x16, 0x1f, 0xea, 0x88, 0x5f, 0x40, 0x29, 0xda, 0x7d, 0x86, 0x79, 0xfb, 0xda, 0xf2, 0x43, 0x1d, 0xf8, 0x13, 0x28, 0x9b, 0x5d, 0x98, 0x6c, 0x2e, 0x5f,
0xc9, 0xe6, 0xe2, 0x35, 0xbb, 0xb5, 0x95, 0x91, 0xeb, 0xcb, 0x2f, 0xa1, 0x3c, 0x5b, 0x72, 0x49, 0xbb, 0xdb, 0x5b, 0x29, 0xb9, 0xbe, 0xfc, 0x14, 0x2a, 0xf3, 0xa5, 0x97, 0x44, 0xb5, 0xa2, 0x8b,
0x5c, 0x2b, 0xbe, 0x28, 0xb7, 0x9a, 0xd9, 0x03, 0x7d, 0x7f, 0x1f, 0x60, 0xbe, 0x66, 0x92, 0xe6, 0x73, 0xbb, 0x95, 0x3e, 0xd0, 0xf7, 0xf7, 0x01, 0x16, 0x6b, 0x27, 0x69, 0x5d, 0xb6, 0xfb, 0xb6,
0x75, 0xbb, 0x6e, 0x6b, 0x7b, 0xc1, 0x89, 0x36, 0x71, 0x00, 0x95, 0xd8, 0x16, 0x49, 0x62, 0x74, 0xb7, 0x97, 0x9c, 0x68, 0x13, 0xcf, 0xa1, 0x1a, 0xd9, 0x2a, 0x49, 0x84, 0x3e, 0x12, 0x6b, 0x6a,
0x91, 0x5a, 0x4b, 0x5b, 0xad, 0x45, 0x47, 0xf3, 0x44, 0x66, 0x2b, 0x07, 0x99, 0xb3, 0x69, 0x72, 0xbb, 0xbd, 0xec, 0x68, 0x91, 0xc8, 0x7c, 0x05, 0x21, 0x0b, 0x76, 0x8d, 0x2f, 0x2a, 0xf3, 0x44,
0x31, 0x99, 0x25, 0x92, 0xdd, 0x4e, 0x9e, 0x41, 0x51, 0xaf, 0x1b, 0x64, 0x43, 0x2b, 0x25, 0x37, 0xd2, 0xdb, 0xca, 0x23, 0x28, 0xe9, 0xf5, 0x83, 0x6c, 0x68, 0xa5, 0xf8, 0x86, 0xd2, 0xde, 0x4c,
0x92, 0xd6, 0x66, 0x5a, 0xac, 0x6f, 0x76, 0xa0, 0x12, 0x63, 0xff, 0x59, 0xfc, 0xd9, 0x17, 0x61, 0x8a, 0xf5, 0xcd, 0x2e, 0x54, 0x23, 0xd3, 0x60, 0x1e, 0x7f, 0x7a, 0x42, 0xcc, 0xab, 0x90, 0xa4,
0x56, 0x85, 0x34, 0xcd, 0x3e, 0x36, 0x90, 0x74, 0xaa, 0xf1, 0x77, 0x98, 0xcc, 0x52, 0xcd, 0x3e, 0xdd, 0xfb, 0x19, 0x24, 0xa1, 0x5a, 0x74, 0x2e, 0x93, 0x79, 0xaa, 0xe9, 0x61, 0x3d, 0x4f, 0x22,
0xce, 0xb3, 0x24, 0x32, 0xaf, 0x28, 0xda, 0x39, 0x85, 0xd5, 0x24, 0xe7, 0xe0, 0x0b, 0x72, 0x0d, 0x35, 0x55, 0xd1, 0xce, 0x31, 0xac, 0xc6, 0x39, 0x08, 0x27, 0xca, 0x25, 0x2c, 0xa6, 0x8c, 0x5d,
0x6b, 0x29, 0x63, 0xb7, 0x3f, 0xcb, 0x69, 0xe4, 0xb9, 0xfa, 0xdb, 0x7a, 0xae, 0xfe, 0x90, 0x12, 0xff, 0x24, 0xc7, 0x91, 0xc7, 0xea, 0x6f, 0xec, 0xa9, 0xfa, 0x83, 0x4a, 0x48, 0xa4, 0x11, 0x8c,
0x12, 0x6b, 0x84, 0xc8, 0xc2, 0x5a, 0x42, 0xa6, 0xee, 0xed, 0x18, 0x18, 0x4b, 0x17, 0xff, 0x72, 0x85, 0xb5, 0x98, 0x4c, 0xdd, 0xdb, 0xc9, 0x60, 0x2c, 0x3d, 0xfc, 0x0b, 0x96, 0x78, 0x6d, 0xe4,
0xa5, 0xc6, 0x8c, 0xfc, 0x27, 0x52, 0x5e, 0x3c, 0x9a, 0xad, 0xff, 0x5e, 0x7b, 0xae, 0x0c, 0xf7, 0x3f, 0x46, 0x79, 0xf9, 0x0b, 0x6d, 0xff, 0xf7, 0xd2, 0x73, 0x65, 0xb8, 0x5f, 0x94, 0xff, 0xb2,
0x97, 0xe5, 0xbf, 0xea, 0xa7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x86, 0xfc, 0xda, 0xe9, 0x62, 0x1f, 0xfe, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x68, 0x21, 0x22, 0x40, 0x72, 0x0f, 0x00, 0x00,
0x0f, 0x00, 0x00,
} }

View File

@ -222,11 +222,11 @@ message WalletBalanceResponse {
double balance = 1; double balance = 1;
} }
message RTChannel { message RoutingTableLink {
string id1 = 1; string id1 = 1;
string id2 = 2; string id2 = 2;
string edgeID = 3; string outpoint = 3;
double capacity = 4; int64 capacity = 4;
double weight = 5; double weight = 5;
} }
@ -234,5 +234,5 @@ message ShowRoutingTableRequest {
} }
message ShowRoutingTableResponse { message ShowRoutingTableResponse {
repeated RTChannel channels = 1; repeated RoutingTableLink channels = 1;
} }

View File

@ -7,6 +7,7 @@ package lnwire
import ( import (
"fmt" "fmt"
"io" "io"
"bytes"
) )
type NeighborAckMessage struct { type NeighborAckMessage struct {
@ -27,12 +28,21 @@ func (msg *NeighborAckMessage) Encode(w io.Writer, pver uint32) error {
} }
func (msg *NeighborAckMessage) Decode(r io.Reader, pver uint32) error { func (msg *NeighborAckMessage) Decode(r io.Reader, pver uint32) error {
buff := make([]byte, 18)
_, err := r.Read(buff)
if err != nil {
return err
}
if !bytes.Equal(buff, []byte("NeighborAckMessage")) {
fmt.Errorf("Can't decode NeighborAckMessage message")
}
return nil return nil
} }
func (msg *NeighborAckMessage) MaxPayloadLength(uint32) uint32 { func (msg *NeighborAckMessage) MaxPayloadLength(uint32) uint32 {
// Some random number. Transmission functions work bad if it is 0 // Length of the string "NeighborAckMessage" used in Encode
return 100 // Transmission functions work bad if it is 0
return 18
} }

View File

@ -512,13 +512,13 @@ func (r *rpcServer) ShowRoutingTable(ctx context.Context,
in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) { in *lnrpc.ShowRoutingTableRequest) (*lnrpc.ShowRoutingTableResponse, error) {
rpcsLog.Debugf("[ShowRoutingTable]") rpcsLog.Debugf("[ShowRoutingTable]")
rtCopy := r.server.routingMgr.GetRTCopy() rtCopy := r.server.routingMgr.GetRTCopy()
channels := make([]*lnrpc.RTChannel, 0) channels := make([]*lnrpc.RoutingTableLink, 0)
for _, channel := range rtCopy.AllChannels() { for _, channel := range rtCopy.AllChannels() {
channels = append(channels, channels = append(channels,
&lnrpc.RTChannel{ &lnrpc.RoutingTableLink{
Id1: channel.Id1.String(), Id1: channel.Id1.String(),
Id2: channel.Id2.String(), Id2: channel.Id2.String(),
EdgeID: channel.EdgeID.String(), Outpoint: channel.EdgeID.String(),
Capacity: channel.Info.Capacity(), Capacity: channel.Info.Capacity(),
Weight: channel.Info.Weight(), Weight: channel.Info.Weight(),
}, },