routing: Move tools inside lnd. Refactor and delete unneeded stuff
Use [33]byte for graph vertex representation. Delete unneeded stuff: 1. DeepEqual for graph comparison 2. EdgePath 3. 2-thread BFS 4. Table transfer messages and neighborhood radius 5. Beacons Refactor: 1. Change ID to Vertex 2. Test use table driven approach 3. Add comments 4. Make graph internal representation private 5. Use wire.OutPoint as EdgeId 6. Decouple routing messages from routing implementation 7. Delete Async methods 8. Delete unneeded channels and priority buffer from manager 9. Delete unneeded interfaces in internal graph realisation 10. Renamed ID to Vertex
This commit is contained in:
parent
da3028e10c
commit
327768f4ad
@ -10,16 +10,16 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/BitfuryLightning/tools/prefix_tree"
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/visualizer/prefix_tree"
|
||||
"github.com/lightningnetwork/lnd/routing/rt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt/visualizer"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/visualizer"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TODO(roasbeef): cli logic for supporting both positional and unix style
|
||||
@ -796,6 +796,26 @@ var ShowRoutingTableCommand = cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func outPointFromString(s string) (*wire.OutPoint, error) {
|
||||
split := strings.Split(s, ":")
|
||||
if len(split) != 2 {
|
||||
return nil, fmt.Errorf("Wrong format of OutPoint. Got %v", s)
|
||||
}
|
||||
h, err := wire.NewShaHashFromStr(split[0])
|
||||
if err!=nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := strconv.Atoi(split[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n<0 {
|
||||
return nil, fmt.Errorf("Got incorrect output number %v", n)
|
||||
}
|
||||
return wire.NewOutPoint(h, uint32(n)), nil
|
||||
}
|
||||
|
||||
|
||||
func getRoutingTable(ctxb context.Context, client lnrpc.LightningClient) (*rt.RoutingTable, error) {
|
||||
req := &lnrpc.ShowRoutingTableRequest{}
|
||||
resp, err := client.ShowRoutingTable(ctxb, req)
|
||||
@ -805,11 +825,23 @@ func getRoutingTable(ctxb context.Context, client lnrpc.LightningClient) (*rt.Ro
|
||||
|
||||
r := rt.NewRoutingTable()
|
||||
for _, channel := range resp.Channels {
|
||||
outPoint, err := outPointFromString(channel.Outpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id1, err := hex.DecodeString(channel.Id1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id2, err := hex.DecodeString(channel.Id2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.AddChannel(
|
||||
graph.NewID(channel.Id1),
|
||||
graph.NewID(channel.Id2),
|
||||
graph.NewEdgeID(channel.Outpoint),
|
||||
&rt.ChannelInfo{channel.Capacity, channel.Weight},
|
||||
graph.NewVertex(id1),
|
||||
graph.NewVertex(id2),
|
||||
graph.NewEdgeID(*outPoint),
|
||||
&graph.ChannelInfo{channel.Capacity, channel.Weight},
|
||||
)
|
||||
}
|
||||
return r, nil
|
||||
@ -903,14 +935,13 @@ func showRoutingTableAsImage(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
func writeToTempFile(r *rt.RoutingTable, file *os.File, self string) error {
|
||||
slc := []graph.ID{graph.NewID(self)}
|
||||
slc := []graph.Vertex{graph.NewVertex([]byte(self))}
|
||||
viz := visualizer.New(r.G, slc, nil, nil)
|
||||
viz.ApplyToNode = func(s string) string { return s }
|
||||
viz.ApplyToEdge = func(info interface{}) string {
|
||||
if info, ok := info.(*rt.ChannelInfo); ok {
|
||||
return fmt.Sprintf(`"%v"`, info.Capacity())
|
||||
viz.ApplyToNode = func(v graph.Vertex) string {
|
||||
return hex.EncodeToString(v.ToByte())
|
||||
}
|
||||
return "nil"
|
||||
viz.ApplyToEdge = func(info *graph.ChannelInfo) string {
|
||||
return fmt.Sprintf(`"%v"`, info.Cpt)
|
||||
}
|
||||
// need to call method if plan to use shortcut, autocomplete, etc
|
||||
viz.BuildPrefixTree()
|
||||
@ -977,18 +1008,18 @@ func printRTAsTable(r *rt.RoutingTable, humanForm bool) {
|
||||
// Generate prefix tree for shortcuts
|
||||
lightningIdTree = prefix_tree.NewPrefixTree()
|
||||
for _, node := range r.Nodes() {
|
||||
lightningIdTree.Add(node.String())
|
||||
lightningIdTree.Add(hex.EncodeToString(node.ToByte()))
|
||||
}
|
||||
edgeIdTree = prefix_tree.NewPrefixTree()
|
||||
for _, channel := range channels {
|
||||
edgeIdTree.Add(channel.EdgeID.String())
|
||||
edgeIdTree.Add(channel.Id.String())
|
||||
}
|
||||
}
|
||||
for _, channel := range channels {
|
||||
var source, target, edgeId string
|
||||
sourceHex := channel.Id1.String()
|
||||
targetHex := channel.Id2.String()
|
||||
edgeIdRaw := channel.EdgeID.String()
|
||||
sourceHex := hex.EncodeToString(channel.Src.ToByte())
|
||||
targetHex := hex.EncodeToString(channel.Tgt.ToByte())
|
||||
edgeIdRaw := channel.Id.String()
|
||||
if humanForm {
|
||||
source = getShortcut(lightningIdTree, sourceHex, minLen)
|
||||
target = getShortcut(lightningIdTree, targetHex, minLen)
|
||||
@ -1031,15 +1062,15 @@ func printRTAsJSON(r *rt.RoutingTable) {
|
||||
channelsRaw := r.AllChannels()
|
||||
channels.Channels = make([]ChannelDesc, 0, len(channelsRaw))
|
||||
for _, channelRaw := range channelsRaw {
|
||||
sourceHex := channelRaw.Id1.String()
|
||||
targetHex := channelRaw.Id2.String()
|
||||
sourceHex := hex.EncodeToString(channelRaw.Src.ToByte())
|
||||
targetHex := hex.EncodeToString(channelRaw.Tgt.ToByte())
|
||||
channels.Channels = append(channels.Channels,
|
||||
ChannelDesc{
|
||||
ID1: sourceHex,
|
||||
ID2: targetHex,
|
||||
EdgeId: channelRaw.EdgeID.String(),
|
||||
Weight: channelRaw.Info.Weight(),
|
||||
Capacity: channelRaw.Info.Capacity(),
|
||||
EdgeId: channelRaw.Id.String(),
|
||||
Weight: channelRaw.Info.Wgt,
|
||||
Capacity: channelRaw.Info.Cpt,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@ -14,9 +13,8 @@ import (
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"google.golang.org/grpc"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -622,11 +620,10 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
||||
chanInfo := openChan.StateSnapshot()
|
||||
capacity := int64(chanInfo.LocalBalance + chanInfo.RemoteBalance)
|
||||
pubSerialized := fmsg.peer.addr.IdentityKey.SerializeCompressed()
|
||||
vertex := hex.EncodeToString(pubSerialized)
|
||||
fmsg.peer.server.routingMgr.OpenChannel(
|
||||
graph.NewID(vertex),
|
||||
graph.NewEdgeID(fundingPoint.String()),
|
||||
&rt.ChannelInfo{
|
||||
graph.NewVertex(pubSerialized),
|
||||
graph.NewEdgeID(*fundingPoint),
|
||||
&graph.ChannelInfo{
|
||||
Cpt: capacity,
|
||||
},
|
||||
)
|
||||
@ -694,11 +691,11 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
||||
// Notify the L3 routing manager of the newly active channel link.
|
||||
capacity := int64(resCtx.reservation.OurContribution().FundingAmount +
|
||||
resCtx.reservation.TheirContribution().FundingAmount)
|
||||
vertex := hex.EncodeToString(fmsg.peer.addr.IdentityKey.SerializeCompressed())
|
||||
vertex := fmsg.peer.addr.IdentityKey.SerializeCompressed()
|
||||
fmsg.peer.server.routingMgr.OpenChannel(
|
||||
graph.NewID(vertex),
|
||||
graph.NewEdgeID(resCtx.reservation.FundingOutpoint().String()),
|
||||
&rt.ChannelInfo{
|
||||
graph.NewVertex(vertex),
|
||||
graph.NewEdgeID(*resCtx.reservation.FundingOutpoint()),
|
||||
&graph.ChannelInfo{
|
||||
Cpt: capacity,
|
||||
},
|
||||
)
|
||||
|
79
glide.lock
generated
79
glide.lock
generated
@ -1,5 +1,5 @@
|
||||
hash: 2106ce14ff53c14d3d0d3d8f34e1cf01c01a79eef409ffe871cd5783b77939c8
|
||||
updated: 2016-10-27T20:34:19.347013604-07:00
|
||||
hash: 0bb53053b11289c7322e08bc1fde538d7538c97180a42fc227fb76582ca39b8e
|
||||
updated: 2016-11-02T08:18:51.538375911-04:00
|
||||
imports:
|
||||
- name: github.com/aead/chacha20
|
||||
version: 7e1038a97ad08a9a16cb88ed7a6778b366ba4d99
|
||||
@ -11,20 +11,10 @@ imports:
|
||||
version: d4d8514752339899250316f88a7907468e8eca7e
|
||||
subpackages:
|
||||
- ast
|
||||
- parser
|
||||
- token
|
||||
- errors
|
||||
- lexer
|
||||
- name: github.com/BitfuryLightning/tools
|
||||
version: b36ae00916b800503504455f7afeb3159bd5ee35
|
||||
subpackages:
|
||||
- routing
|
||||
- rt
|
||||
- rt/graph
|
||||
- prefix_tree
|
||||
- rt/visualizer
|
||||
- pbuffer
|
||||
- pqueue
|
||||
- parser
|
||||
- token
|
||||
- name: github.com/boltdb/bolt
|
||||
version: 583e8937c61f1af6513608ccc75c97b6abdf4ff9
|
||||
- name: github.com/btcsuite/bolt
|
||||
@ -42,13 +32,13 @@ imports:
|
||||
- name: github.com/btcsuite/golangcrypto
|
||||
version: 53f62d9b43e87a6c56975cf862af7edf33a8d0df
|
||||
subpackages:
|
||||
- ripemd160
|
||||
- nacl/secretbox
|
||||
- pbkdf2
|
||||
- poly1305
|
||||
- ripemd160
|
||||
- salsa20/salsa
|
||||
- scrypt
|
||||
- ssh/terminal
|
||||
- poly1305
|
||||
- salsa20/salsa
|
||||
- pbkdf2
|
||||
- name: github.com/btcsuite/seelog
|
||||
version: ae8891d029dd3c269dcfd6f261ad23e761acd99f
|
||||
- name: github.com/btcsuite/websocket
|
||||
@ -66,16 +56,16 @@ imports:
|
||||
- name: github.com/golang/protobuf
|
||||
version: 98fa357170587e470c5f27d3c3ea0947b71eb455
|
||||
subpackages:
|
||||
- proto
|
||||
- jsonpb
|
||||
- proto
|
||||
- protoc-gen-go/descriptor
|
||||
- name: github.com/grpc-ecosystem/grpc-gateway
|
||||
version: a8f25bd1ab549f8b87afd48aa9181221e9d439bb
|
||||
subpackages:
|
||||
- runtime
|
||||
- runtime/internal
|
||||
- third_party/googleapis/google/api
|
||||
- utilities
|
||||
- runtime/internal
|
||||
- name: github.com/howeyc/gopass
|
||||
version: f5387c492211eb133053880d23dfae62aa14123d
|
||||
- name: github.com/lightningnetwork/lightning-onion
|
||||
@ -87,38 +77,38 @@ imports:
|
||||
- btcec
|
||||
- btcjson
|
||||
- chaincfg
|
||||
- database
|
||||
- rpctest
|
||||
- txscript
|
||||
- wire
|
||||
- rpctest
|
||||
- database
|
||||
- name: github.com/roasbeef/btcrpcclient
|
||||
version: 2c6c5c5671f6e86d42b84da30cafcd80e3313b41
|
||||
- name: github.com/roasbeef/btcutil
|
||||
version: 94511fbe6c9bf8ecddb82ccfe8f326e45a4559b0
|
||||
subpackages:
|
||||
- base58
|
||||
- bloom
|
||||
- coinset
|
||||
- hdkeychain
|
||||
- txsort
|
||||
- base58
|
||||
- name: github.com/roasbeef/btcwallet
|
||||
version: 7acd18a96697b180b631631108f1a15448de369f
|
||||
subpackages:
|
||||
- chain
|
||||
- waddrmgr
|
||||
- wallet
|
||||
- walletdb/bdb
|
||||
- walletdb
|
||||
- internal/helpers
|
||||
- internal/legacy/keystore
|
||||
- internal/legacy/rename
|
||||
- internal/prompt
|
||||
- internal/zero
|
||||
- snacl
|
||||
- internal/prompt
|
||||
- waddrmgr
|
||||
- wallet
|
||||
- wallet/internal/txsizes
|
||||
- wallet/txauthor
|
||||
- wallet/txrules
|
||||
- walletdb
|
||||
- walletdb/bdb
|
||||
- wtxmgr
|
||||
- internal/legacy/keystore
|
||||
- internal/helpers
|
||||
- wallet/internal/txsizes
|
||||
- internal/legacy/rename
|
||||
- name: github.com/urfave/cli
|
||||
version: a14d7d367bc02b1f57d88de97926727f2d936387
|
||||
- name: golang.org/x/crypto
|
||||
@ -126,22 +116,22 @@ imports:
|
||||
subpackages:
|
||||
- hkdf
|
||||
- nacl/secretbox
|
||||
- ripemd160
|
||||
- scrypt
|
||||
- poly1305
|
||||
- salsa20/salsa
|
||||
- pbkdf2
|
||||
- poly1305
|
||||
- ripemd160
|
||||
- salsa20/salsa
|
||||
- scrypt
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/net
|
||||
version: b336a971b799939dd16ae9b1df8334cb8b977c4d
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
- trace
|
||||
- http2/hpack
|
||||
- idna
|
||||
- lex/httplex
|
||||
- internal/timeseries
|
||||
- lex/httplex
|
||||
- trace
|
||||
- name: golang.org/x/sys
|
||||
version: c200b10b5d5e122be351b67af224adc6128af5bf
|
||||
subpackages:
|
||||
@ -149,12 +139,17 @@ imports:
|
||||
- name: google.golang.org/grpc
|
||||
version: b7f1379d3cbbbeb2ca3405852012e237aa05459e
|
||||
subpackages:
|
||||
- grpclog
|
||||
- codes
|
||||
- metadata
|
||||
- credentials
|
||||
- grpclog
|
||||
- internal
|
||||
- metadata
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
testImports: []
|
||||
- transport
|
||||
testImports:
|
||||
- name: github.com/BitfuryLightning/tools
|
||||
version: ca92fef460d1f0f2ce28932f941bcc86594b3033
|
||||
subpackages:
|
||||
- rt
|
||||
- rt/graph
|
||||
|
@ -1,11 +1,5 @@
|
||||
package: github.com/lightningnetwork/lnd
|
||||
import:
|
||||
- package: github.com/BitfuryLightning/tools
|
||||
version: b36ae00916b800503504455f7afeb3159bd5ee35
|
||||
subpackages:
|
||||
- routing
|
||||
- rt
|
||||
- rt/graph
|
||||
- package: github.com/boltdb/bolt
|
||||
version: ^1.2.1
|
||||
- package: github.com/btcsuite/btclog
|
||||
|
@ -8,9 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/BitfuryLightning/tools/routing"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lightning-onion"
|
||||
@ -537,9 +536,9 @@ func (h *htlcSwitch) handleUnregisterLink(req *unregisterLinkMsg) {
|
||||
// * distinction between connection close and channel close
|
||||
for _, linkChan := range chansRemoved {
|
||||
err := h.router.RemoveChannel(
|
||||
graph.NewID(hex.EncodeToString(h.gateway)),
|
||||
graph.NewID(hex.EncodeToString(req.remoteID)),
|
||||
graph.NewEdgeID(linkChan.String()),
|
||||
graph.NewVertex(h.gateway),
|
||||
graph.NewVertex(req.remoteID),
|
||||
graph.NewEdgeID(*linkChan),
|
||||
)
|
||||
if err != nil {
|
||||
hswcLog.Errorf("unable to remove channel from "+
|
||||
|
@ -62,6 +62,14 @@ func (c CreditsAmount) ToSatoshi() int64 {
|
||||
return int64(c / 1000)
|
||||
}
|
||||
|
||||
type ChannelOperation struct {
|
||||
NodePubKey1, NodePubKey2 [33]byte
|
||||
ChannelId *wire.OutPoint
|
||||
Capacity int64
|
||||
Weight float64
|
||||
Operation byte
|
||||
}
|
||||
|
||||
// writeElement is a one-stop shop to write the big endian representation of
|
||||
// any element which is to be serialized for the wire protocol. The passed
|
||||
// io.Writer should be backed by an appropriatly sized byte slice, or be able
|
||||
@ -193,6 +201,11 @@ func writeElement(w io.Writer, element interface{}) error {
|
||||
if _, err := w.Write(e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case [33]byte:
|
||||
// TODO(roasbeef): should be factor out to caller logic...
|
||||
if _, err := w.Write(e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case wire.BitcoinNet:
|
||||
var b [4]byte
|
||||
binary.BigEndian.PutUint32(b[:], uint32(e))
|
||||
@ -282,6 +295,34 @@ func writeElement(w io.Writer, element interface{}) error {
|
||||
return err
|
||||
}
|
||||
// TODO(roasbeef): *MsgTx
|
||||
case int64, float64:
|
||||
err := binary.Write(w, binary.BigEndian, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case []ChannelOperation:
|
||||
err := writeElement(w, uint64(len(e)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i:=0; i<len(e); i++ {
|
||||
err := writeElement(w, e[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case ChannelOperation:
|
||||
err := writeElements(w,
|
||||
e.NodePubKey1,
|
||||
e.NodePubKey2,
|
||||
e.ChannelId,
|
||||
e.Capacity,
|
||||
e.Weight,
|
||||
e.Operation,
|
||||
)
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unknown type in writeElement: %T", e)
|
||||
}
|
||||
@ -447,6 +488,10 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
if _, err = io.ReadFull(r, e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case *[33]byte:
|
||||
if _, err = io.ReadFull(r, e[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
case *wire.BitcoinNet:
|
||||
var b [4]byte
|
||||
if _, err := io.ReadFull(r, b[:]); err != nil {
|
||||
@ -536,6 +581,37 @@ func readElement(r io.Reader, element interface{}) error {
|
||||
index := binary.BigEndian.Uint32(idxBytes[:])
|
||||
|
||||
*e = wire.NewOutPoint(hash, index)
|
||||
case *int64, *float64:
|
||||
err := binary.Read(r, binary.BigEndian, e)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *[]ChannelOperation:
|
||||
var nChannels uint64
|
||||
err := readElement(r, &nChannels)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = make([]ChannelOperation, nChannels)
|
||||
for i:=uint64(0); i < nChannels; i++ {
|
||||
err := readElement(r, &((*e)[i]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *ChannelOperation:
|
||||
err := readElements(r,
|
||||
&e.NodePubKey1,
|
||||
&e.NodePubKey2,
|
||||
&e.ChannelId,
|
||||
&e.Capacity,
|
||||
&e.Weight,
|
||||
&e.Operation,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unknown type in readElement: %T", e)
|
||||
}
|
||||
|
@ -114,10 +114,6 @@ func makeEmptyMessage(command uint32) (Message, error) {
|
||||
msg = &NeighborAckMessage{}
|
||||
case CmdNeighborRstMessage:
|
||||
msg = &NeighborRstMessage{}
|
||||
case CmdRoutingTableRequestMessage:
|
||||
msg = &RoutingTableRequestMessage{}
|
||||
case CmdRoutingTableTransferMessage:
|
||||
msg = &RoutingTableTransferMessage{}
|
||||
case CmdPing:
|
||||
msg = &Ping{}
|
||||
case CmdPong:
|
||||
|
@ -7,22 +7,20 @@ package lnwire
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
)
|
||||
|
||||
type NeighborHelloMessage struct {
|
||||
RT *rt.RoutingTable
|
||||
// List of channels
|
||||
Channels []ChannelOperation
|
||||
}
|
||||
|
||||
func (msg *NeighborHelloMessage) Decode(r io.Reader, pver uint32) error {
|
||||
rt1, err := rt.UnmarshallRoutingTable(r)
|
||||
msg.RT = rt1
|
||||
err := readElements(r, &msg.Channels)
|
||||
return err
|
||||
}
|
||||
|
||||
func (msg *NeighborHelloMessage) Encode(w io.Writer, pver uint32) error {
|
||||
err := msg.RT.Marshall(w)
|
||||
err := writeElement(w, msg.Channels)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -41,7 +39,7 @@ func (msg *NeighborHelloMessage) Validate() error {
|
||||
}
|
||||
|
||||
func (msg *NeighborHelloMessage) String() string {
|
||||
return fmt.Sprintf("NeighborHelloMessage{%v}", msg.RT)
|
||||
return fmt.Sprintf("NeighborHelloMessage{%v}", msg.Channels)
|
||||
}
|
||||
|
||||
var _ Message = (*NeighborHelloMessage)(nil)
|
||||
|
@ -7,46 +7,80 @@ package lnwire
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
func samplePubKey(b byte) [33]byte {
|
||||
var a [33]byte
|
||||
for i:=0; i<33; i++ {
|
||||
a[i] = b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func sampleOutPoint(b byte) wire.OutPoint {
|
||||
var w wire.OutPoint
|
||||
for i:=0; i<len(w.Hash); i++ {
|
||||
w.Hash[i] = b
|
||||
}
|
||||
w.Index = uint32(b)
|
||||
return w
|
||||
}
|
||||
|
||||
func genNeighborHelloMessage() *NeighborHelloMessage {
|
||||
p1 := samplePubKey(1)
|
||||
p2 := samplePubKey(2)
|
||||
p3 := samplePubKey(3)
|
||||
e1 := sampleOutPoint(4)
|
||||
e2 := sampleOutPoint(5)
|
||||
|
||||
msg := NeighborHelloMessage{
|
||||
Channels: []ChannelOperation{
|
||||
{
|
||||
NodePubKey1: p1,
|
||||
NodePubKey2: p2,
|
||||
ChannelId: &e1,
|
||||
Capacity: 100000,
|
||||
Weight: 1.0,
|
||||
Operation: 0,
|
||||
},
|
||||
{
|
||||
NodePubKey1: p2,
|
||||
NodePubKey2: p3,
|
||||
ChannelId: &e2,
|
||||
Capacity: 210000,
|
||||
Weight: 2.0,
|
||||
Operation: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
|
||||
func TestNeighborHelloMessageEncodeDecode(t *testing.T) {
|
||||
Id1 := graph.NewID(1)
|
||||
Id2 := graph.NewID(2)
|
||||
rt1 := rt.NewRoutingTable()
|
||||
rt1.AddChannel(Id1, Id2, graph.NewEdgeID("1"), &rt.ChannelInfo{1, 1})
|
||||
msg1 := genNeighborHelloMessage()
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
msg1 := NeighborHelloMessage{RT: rt1}
|
||||
err := msg1.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't encode message ", err)
|
||||
}
|
||||
msg2 := new(NeighborHelloMessage)
|
||||
err = msg2.Decode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't decode message ", err)
|
||||
}
|
||||
if msg2.RT == nil {
|
||||
t.Fatal("After decoding RT should not be nil")
|
||||
}
|
||||
if !msg2.RT.HasChannel(Id1, Id2, graph.NewEdgeID("1")) {
|
||||
t.Errorf("msg2.RT.HasChannel(Id1, Id2) = false, want true")
|
||||
}
|
||||
if !msg2.RT.HasChannel(Id2, Id1, graph.NewEdgeID("1")) {
|
||||
t.Errorf("msg2.RT.HasChannel(Id2, Id1) = false, want true")
|
||||
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(msg1, msg2) {
|
||||
t.Fatalf("encode/decode error messages don't match %v vs %v",
|
||||
msg1, msg2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeighborHelloMessageReadWrite(t *testing.T) {
|
||||
Id1 := graph.NewID(1)
|
||||
Id2 := graph.NewID(2)
|
||||
rt1 := rt.NewRoutingTable()
|
||||
rt1.AddChannel(Id1, Id2, graph.NewEdgeID("1"), &rt.ChannelInfo{1, 1})
|
||||
msg1 := genNeighborHelloMessage()
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
msg1 := &NeighborHelloMessage{RT: rt1}
|
||||
_, err := WriteMessage(b, msg1, 0, wire.SimNet)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write message %v", err)
|
||||
@ -55,17 +89,10 @@ func TestNeighborHelloMessageReadWrite(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Can't read message %v", err)
|
||||
}
|
||||
msg2c, ok := msg2.(*NeighborHelloMessage)
|
||||
if !ok {
|
||||
t.Fatalf("Can't convert to *NeighborHelloMessage")
|
||||
}
|
||||
if msg2c.RT == nil {
|
||||
t.Fatal("After decoding RT should not be nil")
|
||||
}
|
||||
if !msg2c.RT.HasChannel(Id1, Id2, graph.NewEdgeID("1")) {
|
||||
t.Errorf("msg2.RT.HasChannel(Id1, Id2) = false, want true")
|
||||
}
|
||||
if !msg2c.RT.HasChannel(Id2, Id1, graph.NewEdgeID("1")) {
|
||||
t.Errorf("msg2.RT.HasChannel(Id2, Id1) = false, want true")
|
||||
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(msg1, msg2) {
|
||||
t.Fatalf("encode/decode error messages don't match %v vs %v",
|
||||
msg1, msg2)
|
||||
}
|
||||
}
|
||||
|
@ -21,15 +21,26 @@ func (msg *NeighborRstMessage) Command() uint32 {
|
||||
}
|
||||
|
||||
func (msg *NeighborRstMessage) Encode(w io.Writer, pver uint32) error {
|
||||
return nil
|
||||
_, err := w.Write([]byte("NeighborRstMessage"))
|
||||
return err
|
||||
}
|
||||
|
||||
func (msg *NeighborRstMessage) Decode(r io.Reader, pver uint32) error {
|
||||
// 18 is the length of "NeighborRstMessage"
|
||||
var b [18]byte
|
||||
_, err := r.Read(b[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if string(b[:]) != "NeighborRstMessage" {
|
||||
return fmt.Errorf("Incorrect content of NeighborRstMessage")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *NeighborRstMessage) MaxPayloadLength(uint32) uint32 {
|
||||
return 0
|
||||
// 18 is the length of "NeighborRstMessage"
|
||||
return 18
|
||||
}
|
||||
|
||||
func (msg *NeighborRstMessage) Validate() error {
|
||||
|
44
lnwire/neighbor_rst_test.go
Normal file
44
lnwire/neighbor_rst_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
)
|
||||
|
||||
func TestNeighborRstMessageEncodeDecode(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
msg1 := NeighborRstMessage{}
|
||||
err := msg1.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't encode message ", err)
|
||||
}
|
||||
msg2 := new(NeighborRstMessage)
|
||||
err = msg2.Decode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't decode message %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNeighborRstMessageReadWrite(t *testing.T){
|
||||
b := new(bytes.Buffer)
|
||||
msg1 := &NeighborRstMessage{}
|
||||
_, err := WriteMessage(b, msg1, 0, wire.SimNet)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write message %v", err)
|
||||
}
|
||||
_, msg2, _, err := ReadMessage(b, 0, wire.SimNet)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't read message %v", err)
|
||||
}
|
||||
_, ok := msg2.(*NeighborRstMessage)
|
||||
if !ok {
|
||||
t.Fatalf("Can't convert to *NeighborRstMessage")
|
||||
}
|
||||
}
|
||||
|
@ -5,28 +5,21 @@
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
)
|
||||
|
||||
type NeighborUpdMessage struct {
|
||||
DiffBuff *rt.DifferenceBuffer
|
||||
Updates []ChannelOperation
|
||||
}
|
||||
|
||||
func (msg *NeighborUpdMessage) Decode(r io.Reader, pver uint32) error {
|
||||
decoder := gob.NewDecoder(r)
|
||||
diffBuff := new(rt.DifferenceBuffer)
|
||||
err := decoder.Decode(diffBuff)
|
||||
msg.DiffBuff = diffBuff
|
||||
err := readElements(r, &msg.Updates)
|
||||
return err
|
||||
}
|
||||
|
||||
func (msg *NeighborUpdMessage) Encode(w io.Writer, pver uint32) error {
|
||||
encoder := gob.NewEncoder(w)
|
||||
err := encoder.Encode(msg.DiffBuff)
|
||||
err := writeElements(w, msg.Updates)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -45,7 +38,7 @@ func (msg *NeighborUpdMessage) Validate() error {
|
||||
}
|
||||
|
||||
func (msg *NeighborUpdMessage) String() string {
|
||||
return fmt.Sprintf("NeighborUpdMessage{%v}", *msg.DiffBuff)
|
||||
return fmt.Sprintf("NeighborUpdMessage{%v}", msg.Updates)
|
||||
}
|
||||
|
||||
var _ Message = (*NeighborUpdMessage)(nil)
|
||||
|
87
lnwire/neighbor_upd_test.go
Normal file
87
lnwire/neighbor_upd_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func genNeighborUpdMessage() *NeighborUpdMessage {
|
||||
p1 := samplePubKey(1)
|
||||
p2 := samplePubKey(2)
|
||||
p3 := samplePubKey(3)
|
||||
e1 := sampleOutPoint(4)
|
||||
e2 := sampleOutPoint(5)
|
||||
|
||||
msg := NeighborUpdMessage{
|
||||
Updates: []ChannelOperation{
|
||||
{
|
||||
NodePubKey1: p1,
|
||||
NodePubKey2: p2,
|
||||
ChannelId: &e1,
|
||||
Capacity: 100000,
|
||||
Weight: 1.0,
|
||||
Operation: 0,
|
||||
},
|
||||
{
|
||||
NodePubKey1: p2,
|
||||
NodePubKey2: p3,
|
||||
ChannelId: &e2,
|
||||
Capacity: 210000,
|
||||
Weight: 2.0,
|
||||
Operation: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
|
||||
func TestNeighborUpdMessageEncodeDecode(t *testing.T) {
|
||||
msg1 := genNeighborUpdMessage()
|
||||
b := new(bytes.Buffer)
|
||||
err := msg1.Encode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't encode message: %v", err)
|
||||
}
|
||||
msg2 := new(NeighborUpdMessage)
|
||||
err = msg2.Decode(b, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't decode message: %v", err)
|
||||
}
|
||||
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(msg1, msg2) {
|
||||
t.Fatalf("encode/decode error messages don't match %v vs %v",
|
||||
msg1, msg2)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNeighborUpdMessageReadWrite(t *testing.T) {
|
||||
msg1 := genNeighborUpdMessage()
|
||||
b := new(bytes.Buffer)
|
||||
_, err := WriteMessage(b, msg1, 0, wire.SimNet)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write message %v", err)
|
||||
}
|
||||
_, msg2, _, err := ReadMessage(b, 0, wire.SimNet)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't read message %v", err)
|
||||
}
|
||||
_, ok := msg2.(*NeighborUpdMessage)
|
||||
if !ok {
|
||||
t.Fatalf("Can't convert to *NeighborUpdMessage")
|
||||
}
|
||||
// Assert equality of the two instances.
|
||||
if !reflect.DeepEqual(msg1, msg2) {
|
||||
t.Fatalf("encode/decode error messages don't match %v vs %v",
|
||||
msg1, msg2)
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type RoutingTableRequestMessage struct {
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) String() string {
|
||||
return fmt.Sprintf("RoutingTableRequestMessage{}")
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) Command() uint32 {
|
||||
return CmdRoutingTableRequestMessage
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) Encode(w io.Writer, pver uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) Decode(r io.Reader, pver uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) MaxPayloadLength(uint32) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (msg *RoutingTableRequestMessage) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Message = (*RoutingTableRequestMessage)(nil)
|
@ -1,51 +0,0 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package lnwire
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
)
|
||||
|
||||
type RoutingTableTransferMessage struct {
|
||||
RT *rt.RoutingTable
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) String() string {
|
||||
return fmt.Sprintf("RoutingTableTransferMessage{%v %v %v}", msg.RT)
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) Decode(r io.Reader, pver uint32) error {
|
||||
decoder := gob.NewDecoder(r)
|
||||
rt1 := rt.NewRoutingTable()
|
||||
err := decoder.Decode(rt1.G)
|
||||
msg.RT = rt1
|
||||
return err
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) Encode(w io.Writer, pver uint32) error {
|
||||
encoder := gob.NewEncoder(w)
|
||||
err := encoder.Encode(msg.RT.G)
|
||||
return err
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) Command() uint32 {
|
||||
return CmdRoutingTableTransferMessage
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) MaxPayloadLength(uint32) uint32 {
|
||||
// TODO: Insert some estimations
|
||||
return 1000000
|
||||
}
|
||||
|
||||
func (msg *RoutingTableTransferMessage) Validate() error {
|
||||
// TODO: Add validation
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Message = (*RoutingTableTransferMessage)(nil)
|
19
peer.go
19
peer.go
@ -5,15 +5,13 @@ import (
|
||||
"container/list"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lightning-onion"
|
||||
@ -237,11 +235,10 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
|
||||
chanInfo := lnChan.StateSnapshot()
|
||||
capacity := int64(chanInfo.LocalBalance + chanInfo.RemoteBalance)
|
||||
pubSerialized := p.addr.IdentityKey.SerializeCompressed()
|
||||
vertex := hex.EncodeToString(pubSerialized)
|
||||
p.server.routingMgr.OpenChannel(
|
||||
graph.NewID(vertex),
|
||||
graph.NewEdgeID(chanInfo.ChannelPoint.String()),
|
||||
&rt.ChannelInfo{
|
||||
graph.NewVertex(pubSerialized),
|
||||
graph.NewEdgeID(*chanInfo.ChannelPoint),
|
||||
&graph.ChannelInfo{
|
||||
Cpt: capacity,
|
||||
},
|
||||
)
|
||||
@ -415,13 +412,11 @@ out:
|
||||
case *lnwire.NeighborAckMessage,
|
||||
*lnwire.NeighborHelloMessage,
|
||||
*lnwire.NeighborRstMessage,
|
||||
*lnwire.NeighborUpdMessage,
|
||||
*lnwire.RoutingTableRequestMessage,
|
||||
*lnwire.RoutingTableTransferMessage:
|
||||
*lnwire.NeighborUpdMessage:
|
||||
|
||||
// Convert to base routing message and set sender and receiver
|
||||
vertex := hex.EncodeToString(p.addr.IdentityKey.SerializeCompressed())
|
||||
p.server.routingMgr.ReceiveRoutingMessage(msg, graph.NewID(vertex))
|
||||
vertex := p.addr.IdentityKey.SerializeCompressed()
|
||||
p.server.routingMgr.ReceiveRoutingMessage(msg, graph.NewVertex(vertex))
|
||||
}
|
||||
|
||||
if isChanUpdate {
|
||||
|
470
routing/manager.go
Normal file
470
routing/manager.go
Normal file
@ -0,0 +1,470 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
)
|
||||
|
||||
func channelOperationsFromRT(r *rt.RoutingTable) []lnwire.ChannelOperation {
|
||||
channels := r.AllChannels()
|
||||
chOps := make([]lnwire.ChannelOperation, len(channels))
|
||||
for i:=0; i<len(channels); i++ {
|
||||
var info *graph.ChannelInfo
|
||||
if channels[i].Info != nil {
|
||||
info = channels[i].Info
|
||||
} else {
|
||||
info = new(graph.ChannelInfo)
|
||||
}
|
||||
chOp := lnwire.ChannelOperation{
|
||||
NodePubKey1: channels[i].Src.ToByte33(),
|
||||
NodePubKey2: channels[i].Tgt.ToByte33(),
|
||||
ChannelId: (*wire.OutPoint)(&channels[i].Id),
|
||||
Capacity: info.Cpt,
|
||||
Weight: info.Wgt,
|
||||
Operation: byte(rt.AddChannelOP),
|
||||
}
|
||||
chOps[i] = chOp
|
||||
}
|
||||
return chOps
|
||||
}
|
||||
|
||||
func channelOperationsFromDiffBuff(r rt.DifferenceBuffer) []lnwire.ChannelOperation {
|
||||
chOps := make([]lnwire.ChannelOperation, len(r))
|
||||
for i:=0; i<len(r); i++ {
|
||||
var info *graph.ChannelInfo
|
||||
if r[i].Info != nil {
|
||||
info = r[i].Info
|
||||
} else {
|
||||
info = new(graph.ChannelInfo)
|
||||
}
|
||||
chOp := lnwire.ChannelOperation{
|
||||
NodePubKey1: r[i].Src.ToByte33(),
|
||||
NodePubKey2: r[i].Tgt.ToByte33(),
|
||||
ChannelId: (*wire.OutPoint)(&r[i].Id),
|
||||
Capacity: info.Cpt,
|
||||
Weight: info.Wgt,
|
||||
Operation: byte(r[i].Operation),
|
||||
}
|
||||
chOps[i] = chOp
|
||||
}
|
||||
return chOps
|
||||
}
|
||||
|
||||
func rtFromChannelOperations(chOps []lnwire.ChannelOperation) *rt.RoutingTable {
|
||||
r := rt.NewRoutingTable()
|
||||
for i := 0; i<len(chOps); i++{
|
||||
r.AddChannel(
|
||||
graph.NewVertex(chOps[i].NodePubKey1[:]),
|
||||
graph.NewVertex(chOps[i].NodePubKey2[:]),
|
||||
graph.EdgeID(*chOps[i].ChannelId),
|
||||
&graph.ChannelInfo{
|
||||
Cpt: chOps[i].Capacity,
|
||||
Wgt: chOps[i].Weight,
|
||||
},
|
||||
)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func diffBuffFromChannelOperations(chOps []lnwire.ChannelOperation) *rt.DifferenceBuffer {
|
||||
d := rt.NewDifferenceBuffer()
|
||||
for i := 0; i<len(chOps); i++ {
|
||||
op := rt.NewChannelOperation(
|
||||
graph.NewVertex(chOps[i].NodePubKey1[:]),
|
||||
graph.NewVertex(chOps[i].NodePubKey2[:]),
|
||||
graph.EdgeID(*chOps[i].ChannelId),
|
||||
&graph.ChannelInfo{
|
||||
Cpt: chOps[i].Capacity,
|
||||
Wgt: chOps[i].Weight,
|
||||
},
|
||||
rt.OperationType(chOps[i].Operation),
|
||||
)
|
||||
*d = append(*d, op)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// RoutingMessage is a wrapper around lnwire.Message which
|
||||
// includes sender and receiver.
|
||||
type RoutingMessage struct {
|
||||
SenderID graph.Vertex
|
||||
ReceiverID graph.Vertex
|
||||
Msg lnwire.Message
|
||||
}
|
||||
|
||||
type addChannelCmd struct {
|
||||
Id1, Id2 graph.Vertex
|
||||
TxID graph.EdgeID
|
||||
Info *graph.ChannelInfo
|
||||
err chan error
|
||||
}
|
||||
|
||||
type removeChannelCmd struct {
|
||||
Id1, Id2 graph.Vertex
|
||||
TxID graph.EdgeID
|
||||
err chan error
|
||||
}
|
||||
|
||||
type hasChannelCmd struct {
|
||||
Id1, Id2 graph.Vertex
|
||||
TxID graph.EdgeID
|
||||
rez chan bool
|
||||
err chan error
|
||||
}
|
||||
|
||||
type openChannelCmd struct {
|
||||
Id graph.Vertex
|
||||
TxID graph.EdgeID
|
||||
info *graph.ChannelInfo
|
||||
err chan error
|
||||
}
|
||||
|
||||
type findPathCmd struct {
|
||||
Id graph.Vertex
|
||||
rez chan []graph.Vertex
|
||||
err chan error
|
||||
}
|
||||
|
||||
type findKShortestPathsCmd struct {
|
||||
Id graph.Vertex
|
||||
k int
|
||||
rez chan [][]graph.Vertex
|
||||
err chan error
|
||||
}
|
||||
|
||||
type getRTCopyCmd struct {
|
||||
rez chan *rt.RoutingTable
|
||||
}
|
||||
|
||||
type NeighborState int
|
||||
|
||||
const (
|
||||
StateINIT NeighborState = 0
|
||||
StateACK NeighborState = 1
|
||||
StateWAIT NeighborState = 2
|
||||
)
|
||||
|
||||
type neighborDescription struct {
|
||||
Id graph.Vertex
|
||||
DiffBuff *rt.DifferenceBuffer
|
||||
State NeighborState
|
||||
}
|
||||
|
||||
// RoutingConfig contains configuration information for RoutingManager.
|
||||
type RoutingConfig struct {
|
||||
// SendMessage is used by the routing manager to send a
|
||||
// message to a direct neighbor.
|
||||
SendMessage func([33]byte, lnwire.Message) error
|
||||
}
|
||||
|
||||
// RoutingManager implements routing functionality.
|
||||
type RoutingManager struct {
|
||||
// Current node.
|
||||
Id graph.Vertex
|
||||
// Neighbors of the current node.
|
||||
neighbors map[graph.Vertex]*neighborDescription
|
||||
// Routing table.
|
||||
rT *rt.RoutingTable
|
||||
// Configuration parameters.
|
||||
config *RoutingConfig
|
||||
// Channel for input messages
|
||||
chIn chan interface{}
|
||||
// Closing this channel will stop RoutingManager.
|
||||
chQuit chan struct{}
|
||||
// When RoutingManager stops this channel is closed.
|
||||
ChDone chan struct{}
|
||||
}
|
||||
|
||||
// NewRoutingManager creates new RoutingManager
|
||||
// with empyt routing table.
|
||||
func NewRoutingManager(Id graph.Vertex, config *RoutingConfig) *RoutingManager {
|
||||
return &RoutingManager{
|
||||
Id: Id,
|
||||
neighbors: make(map[graph.Vertex]*neighborDescription),
|
||||
rT: rt.NewRoutingTable(),
|
||||
config: config,
|
||||
chIn: make(chan interface{}, 10),
|
||||
chQuit: make(chan struct{}, 1),
|
||||
ChDone: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Start - start message loop.
|
||||
func (r *RoutingManager) Start() {
|
||||
go func() {
|
||||
out:
|
||||
for {
|
||||
// Prioritise quit.
|
||||
select {
|
||||
case <-r.chQuit:
|
||||
break out
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case msg := <-r.chIn:
|
||||
r.handleMessage(msg)
|
||||
case <-r.chQuit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
close(r.ChDone)
|
||||
}()
|
||||
}
|
||||
|
||||
// Stop stops RoutingManager.
|
||||
// Note if some messages were not processed they will be skipped.
|
||||
func (r *RoutingManager) Stop() {
|
||||
close(r.chQuit)
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleMessage(msg interface{}) {
|
||||
switch msg := msg.(type) {
|
||||
case *openChannelCmd:
|
||||
r.handleOpenChannelCmdMessage(msg)
|
||||
case *addChannelCmd:
|
||||
r.handleAddChannelCmdMessage(msg)
|
||||
case *hasChannelCmd:
|
||||
r.handleHasChannelCmdMessage(msg)
|
||||
case *removeChannelCmd:
|
||||
r.handleRemoveChannelCmdMessage(msg)
|
||||
case *findPathCmd:
|
||||
r.handleFindPath(msg)
|
||||
case *findKShortestPathsCmd:
|
||||
r.handleFindKShortestPaths(msg)
|
||||
case *getRTCopyCmd:
|
||||
r.handleGetRTCopy(msg)
|
||||
case *RoutingMessage:
|
||||
r.handleRoutingMessage(msg)
|
||||
default:
|
||||
fmt.Println("Unknown message type ", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// notifyNeighbors checks if there are
|
||||
// pending changes for each neighbor and send them.
|
||||
// Each neighbor has three states
|
||||
// StateINIT - initial state. No messages has been send to this neighbor
|
||||
// StateWAIT - node waits fo acknowledgement.
|
||||
// StateACK - acknowledgement has been obtained. New updates can be send.
|
||||
func (r *RoutingManager) notifyNeighbors() {
|
||||
for _, neighbor := range r.neighbors {
|
||||
if neighbor.State == StateINIT {
|
||||
neighbor.DiffBuff.Clear()
|
||||
msg := &lnwire.NeighborHelloMessage{
|
||||
Channels: channelOperationsFromRT(r.rT),
|
||||
}
|
||||
r.sendRoutingMessage(msg, neighbor.Id)
|
||||
neighbor.State = StateWAIT
|
||||
continue
|
||||
}
|
||||
if neighbor.State == StateACK && !neighbor.DiffBuff.IsEmpty() {
|
||||
msg := &lnwire.NeighborUpdMessage{
|
||||
Updates: channelOperationsFromDiffBuff(*neighbor.DiffBuff),
|
||||
}
|
||||
r.sendRoutingMessage(msg, neighbor.Id)
|
||||
neighbor.DiffBuff.Clear()
|
||||
neighbor.State = StateWAIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddChannel add channel to routing tables.
|
||||
func (r *RoutingManager) AddChannel(Id1, Id2 graph.Vertex, TxID graph.EdgeID, info *graph.ChannelInfo) error {
|
||||
msg := &addChannelCmd{
|
||||
Id1: Id1,
|
||||
Id2: Id2,
|
||||
TxID: TxID,
|
||||
Info: info,
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.err
|
||||
}
|
||||
|
||||
// HasChannel checks if there are channel in routing table
|
||||
func (r *RoutingManager) HasChannel(Id1, Id2 graph.Vertex, TxID graph.EdgeID) bool {
|
||||
msg := &hasChannelCmd{
|
||||
Id1: Id1,
|
||||
Id2: Id2,
|
||||
TxID: TxID,
|
||||
rez: make(chan bool, 1),
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.rez
|
||||
}
|
||||
|
||||
// RemoveChannel removes channel from routing table
|
||||
func (r *RoutingManager) RemoveChannel(Id1, Id2 graph.Vertex, TxID graph.EdgeID) error {
|
||||
msg := &removeChannelCmd{
|
||||
Id1: Id1,
|
||||
Id2: Id2,
|
||||
TxID: TxID,
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.err
|
||||
}
|
||||
|
||||
// OpenChannel is used to open channel from this node to other node.
|
||||
// It adds node to neighbors and starts routing tables exchange.
|
||||
func (r *RoutingManager) OpenChannel(Id graph.Vertex, TxID graph.EdgeID, info *graph.ChannelInfo) error {
|
||||
msg := &openChannelCmd{
|
||||
Id: Id,
|
||||
TxID: TxID,
|
||||
info: info,
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.err
|
||||
}
|
||||
|
||||
// FindPath finds path from this node to some other node
|
||||
func (r *RoutingManager) FindPath(destId graph.Vertex) ([]graph.Vertex, error) {
|
||||
msg := &findPathCmd{
|
||||
Id: destId,
|
||||
rez: make(chan []graph.Vertex, 1),
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.rez, <-msg.err
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleFindPath(msg *findPathCmd) {
|
||||
path, err := r.rT.ShortestPath(r.Id, msg.Id)
|
||||
msg.rez <- path
|
||||
msg.err <- err
|
||||
}
|
||||
|
||||
// FindKShortesPaths tries to find k paths from this node to destination.
|
||||
// If timeouts returns all found paths
|
||||
func (r *RoutingManager) FindKShortestPaths(destId graph.Vertex, k int) ([][]graph.Vertex, error) {
|
||||
msg := &findKShortestPathsCmd{
|
||||
Id: destId,
|
||||
k: k,
|
||||
rez: make(chan [][]graph.Vertex, 1),
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.rez, <-msg.err
|
||||
}
|
||||
|
||||
// Find k-shortest path.
|
||||
func (r *RoutingManager) handleFindKShortestPaths(msg *findKShortestPathsCmd) {
|
||||
paths, err := r.rT.KShortestPaths(r.Id, msg.Id, msg.k)
|
||||
msg.rez <- paths
|
||||
msg.err <- err
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleGetRTCopy(msg *getRTCopyCmd) {
|
||||
msg.rez <- r.rT.Copy()
|
||||
}
|
||||
|
||||
// GetRTCopy - returns copy of current node routing table.
|
||||
// Note: difference buffers are not copied.
|
||||
func (r *RoutingManager) GetRTCopy() *rt.RoutingTable {
|
||||
msg := &getRTCopyCmd{
|
||||
rez: make(chan *rt.RoutingTable, 1),
|
||||
}
|
||||
r.chIn <- msg
|
||||
return <-msg.rez
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleOpenChannelCmdMessage(msg *openChannelCmd) {
|
||||
// TODO: validate that channel do not exist
|
||||
r.rT.AddChannel(r.Id, msg.Id, msg.TxID, msg.info)
|
||||
// TODO(mkl): what to do if neighbot already exists.
|
||||
r.neighbors[msg.Id] = &neighborDescription{
|
||||
Id: msg.Id,
|
||||
DiffBuff: r.rT.NewDiffBuff(),
|
||||
State: StateINIT,
|
||||
}
|
||||
r.notifyNeighbors()
|
||||
msg.err <- nil
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleAddChannelCmdMessage(msg *addChannelCmd) {
|
||||
r.rT.AddChannel(msg.Id1, msg.Id2, msg.TxID, msg.Info)
|
||||
msg.err <- nil
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleHasChannelCmdMessage(msg *hasChannelCmd) {
|
||||
msg.rez <- r.rT.HasChannel(msg.Id1, msg.Id2, msg.TxID)
|
||||
msg.err <- nil
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleRemoveChannelCmdMessage(msg *removeChannelCmd) {
|
||||
r.rT.RemoveChannel(msg.Id1, msg.Id2, msg.TxID)
|
||||
r.notifyNeighbors()
|
||||
msg.err <- nil
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleNeighborHelloMessage(msg *lnwire.NeighborHelloMessage, senderID graph.Vertex) {
|
||||
// Sometimes we can obtain NeighborHello message from node that is
|
||||
// not our neighbor yet. Because channel creation workflow
|
||||
// end in different times for nodes.
|
||||
t := rtFromChannelOperations(msg.Channels)
|
||||
r.rT.AddTable(t)
|
||||
r.sendRoutingMessage(&lnwire.NeighborAckMessage{}, senderID)
|
||||
r.notifyNeighbors()
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleNeighborUpdMessage(msg *lnwire.NeighborUpdMessage, senderID graph.Vertex) {
|
||||
if _, ok := r.neighbors[senderID]; ok {
|
||||
diffBuff := diffBuffFromChannelOperations(msg.Updates)
|
||||
r.rT.ApplyDiffBuff(diffBuff)
|
||||
r.sendRoutingMessage(&lnwire.NeighborAckMessage{}, senderID)
|
||||
r.notifyNeighbors()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleNeighborRstMessage(msg *lnwire.NeighborRstMessage, senderID graph.Vertex) {
|
||||
if _, ok := r.neighbors[senderID]; ok {
|
||||
r.neighbors[senderID].State = StateINIT
|
||||
r.notifyNeighbors()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleNeighborAckMessage(msg *lnwire.NeighborAckMessage, senderID graph.Vertex) {
|
||||
if _, ok := r.neighbors[senderID]; ok && r.neighbors[senderID].State == StateWAIT {
|
||||
r.neighbors[senderID].State = StateACK
|
||||
// In case there are new updates for node which
|
||||
// appears between sending NeighborUpd and NeighborAck
|
||||
r.notifyNeighbors()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoutingManager) handleRoutingMessage(rmsg *RoutingMessage) {
|
||||
msg := rmsg.Msg
|
||||
switch msg := msg.(type) {
|
||||
case *lnwire.NeighborHelloMessage:
|
||||
r.handleNeighborHelloMessage(msg, rmsg.SenderID)
|
||||
case *lnwire.NeighborUpdMessage:
|
||||
r.handleNeighborUpdMessage(msg, rmsg.SenderID)
|
||||
case *lnwire.NeighborRstMessage:
|
||||
r.handleNeighborRstMessage(msg, rmsg.SenderID)
|
||||
case *lnwire.NeighborAckMessage:
|
||||
r.handleNeighborAckMessage(msg, rmsg.SenderID)
|
||||
default:
|
||||
fmt.Printf("Unknown message type %T\n inside RoutingMessage", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RoutingManager) sendRoutingMessage(msg lnwire.Message, receiverId graph.Vertex) {
|
||||
r.config.SendMessage(receiverId.ToByte33(), msg)
|
||||
}
|
||||
|
||||
func (r *RoutingManager) ReceiveRoutingMessage(msg lnwire.Message, senderID graph.Vertex) {
|
||||
r.chIn <- &RoutingMessage{
|
||||
SenderID: senderID,
|
||||
ReceiverID: r.Id,
|
||||
Msg: msg,
|
||||
}
|
||||
}
|
239
routing/manager_test.go
Normal file
239
routing/manager_test.go
Normal file
@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func vertexFromInt(x int) graph.Vertex {
|
||||
s := fmt.Sprintf("%v", x)
|
||||
return graph.NewVertex([]byte(s))
|
||||
}
|
||||
|
||||
func edgeIdFromString(s string) graph.EdgeID {
|
||||
e := graph.EdgeID{}
|
||||
copy(e.Hash[:], []byte(s))
|
||||
return e
|
||||
}
|
||||
|
||||
var sampleEdgeId graph.EdgeID = edgeIdFromString("EdgeId")
|
||||
|
||||
func createLinearNetwork(n int) (*MockNetwork, []*RoutingManager) {
|
||||
// Creates linear graph 0->1->2->..->n-1
|
||||
nodes := make([]*RoutingManager, 0)
|
||||
net := NewMockNetwork(false)
|
||||
net.Start()
|
||||
for i := 0; i < n; i++ {
|
||||
node := NewRoutingManager(vertexFromInt(i), nil)
|
||||
nodes = append(nodes, node)
|
||||
node.Start()
|
||||
net.Add(node)
|
||||
}
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
nodes[i].OpenChannel(nodes[i+1].Id, sampleEdgeId, nil)
|
||||
nodes[i+1].OpenChannel(nodes[i].Id, sampleEdgeId, nil)
|
||||
}
|
||||
|
||||
return net, nodes
|
||||
}
|
||||
|
||||
func createCompleteNetwork(n int) (*MockNetwork, []*RoutingManager) {
|
||||
nodes := make([]*RoutingManager, 0)
|
||||
net := NewMockNetwork(false)
|
||||
net.Start()
|
||||
for i := 0; i < n; i++ {
|
||||
node := NewRoutingManager(vertexFromInt(i), nil)
|
||||
nodes = append(nodes, node)
|
||||
node.Start()
|
||||
net.Add(node)
|
||||
}
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
for j := i + 1; j < n; j++ {
|
||||
nodes[i].OpenChannel(nodes[j].Id, sampleEdgeId, nil)
|
||||
nodes[j].OpenChannel(nodes[i].Id, sampleEdgeId, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return net, nodes
|
||||
}
|
||||
|
||||
func createNetwork(desc [][2]int, idFunc func(int) graph.Vertex) (*MockNetwork, map[int]*RoutingManager, []graph.Edge) {
|
||||
// Creates network of nodes from graph description
|
||||
net := NewMockNetwork(false)
|
||||
net.Start()
|
||||
// create unique nodes
|
||||
nodes := make(map[int]*RoutingManager)
|
||||
for i := 0; i < len(desc); i++ {
|
||||
for j := 0; j < 2; j++ {
|
||||
nodeId := desc[i][j]
|
||||
if _, ok := nodes[nodeId]; !ok {
|
||||
var id graph.Vertex
|
||||
if idFunc != nil {
|
||||
id = idFunc(nodeId)
|
||||
} else {
|
||||
id = vertexFromInt(nodeId)
|
||||
}
|
||||
node := NewRoutingManager(id, nil)
|
||||
nodes[nodeId] = node
|
||||
node.Start()
|
||||
net.Add(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
edges := make([]graph.Edge, 0, len(desc))
|
||||
for i := 0; i < len(desc); i++ {
|
||||
edgeID := edgeIdFromString(fmt.Sprintf("edge-%v", i))
|
||||
nodes[desc[i][0]].OpenChannel(nodes[desc[i][1]].Id, edgeID, &graph.ChannelInfo{1, 1})
|
||||
nodes[desc[i][1]].OpenChannel(nodes[desc[i][0]].Id, edgeID, &graph.ChannelInfo{1, 1})
|
||||
edges = append(edges, graph.NewEdge(
|
||||
nodes[desc[i][0]].Id,
|
||||
nodes[desc[i][1]].Id,
|
||||
edgeID,
|
||||
&graph.ChannelInfo{1, 1},
|
||||
))
|
||||
}
|
||||
return net, nodes, edges
|
||||
}
|
||||
|
||||
func TestNeighborsScanLinearGraph(t *testing.T) {
|
||||
n := 4
|
||||
net, nodes := createLinearNetwork(n)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
// Each node should know about all channels
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < n; j++ {
|
||||
for k := 0; k < n; k++ {
|
||||
ans := nodes[i].HasChannel(nodes[j].Id, nodes[k].Id, sampleEdgeId)
|
||||
correctAns := abs(j-k) == 1
|
||||
if ans != correctAns {
|
||||
t.Errorf("nodes[%v].HasChannel(%v, %v)==%v, want %v", i, j, k, ans, correctAns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
net.Stop()
|
||||
|
||||
}
|
||||
|
||||
func TestNeighborsScanCompleteGraph(t *testing.T) {
|
||||
n := 4
|
||||
net, nodes := createCompleteNetwork(n)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
// Each node should know about all channels
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < n; j++ {
|
||||
for k := 0; k < n; k++ {
|
||||
ans := nodes[i].HasChannel(nodes[j].Id, nodes[k].Id, sampleEdgeId)
|
||||
correctAns := j != k
|
||||
if ans != correctAns {
|
||||
t.Errorf("nodes[%v].HasChannel(%v, %v)==%v, want %v", i, j, k, ans, correctAns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
net.Stop()
|
||||
}
|
||||
|
||||
func TestNeighborsRemoveChannel(t *testing.T) {
|
||||
// Create complete graph, than delete channels to make it linear
|
||||
n := 4
|
||||
net, nodes := createCompleteNetwork(n)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < n; j++ {
|
||||
if abs(i-j) != 1 {
|
||||
nodes[i].RemoveChannel(nodes[i].Id, nodes[j].Id, sampleEdgeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
// Each node should know about all channels
|
||||
for i := 0; i < n; i++ {
|
||||
for j := 0; j < n; j++ {
|
||||
for k := 0; k < n; k++ {
|
||||
ans := nodes[i].HasChannel(nodes[j].Id, nodes[k].Id, sampleEdgeId)
|
||||
correctAns := abs(j-k) == 1
|
||||
if ans != correctAns {
|
||||
t.Errorf("nodes[%v].HasChannel(%v, %v)==%v, want %v", i, j, k, ans, correctAns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
net.Stop()
|
||||
}
|
||||
|
||||
func TestFindPath(t *testing.T) {
|
||||
// Create linear graph
|
||||
n := 6
|
||||
net, nodes := createLinearNetwork(n)
|
||||
time.Sleep(10 * time.Millisecond) // Each node should know about all channels
|
||||
|
||||
path, err := nodes[0].FindPath(nodes[5].Id)
|
||||
if err != nil {
|
||||
t.Errorf("err = %v, want %v", err)
|
||||
}
|
||||
correctPath := []graph.Vertex{}
|
||||
for i := 0; i < n; i++ {
|
||||
correctPath = append(correctPath, nodes[i].Id)
|
||||
}
|
||||
if !reflect.DeepEqual(path, correctPath) {
|
||||
t.Errorf("path = %v, want %v", path, correctPath)
|
||||
}
|
||||
// Case when path do not exist
|
||||
path, err = nodes[0].FindPath(vertexFromInt(7))
|
||||
if path != nil {
|
||||
t.Errorf("path = %v, want %v", path, nil)
|
||||
}
|
||||
if err != graph.PathNotFoundError {
|
||||
t.Errorf("err = %v, want %v", err, graph.PathNotFoundError)
|
||||
}
|
||||
net.Stop()
|
||||
}
|
||||
|
||||
func TestKShortestPaths(t *testing.T) {
|
||||
net, nodes, _ := createNetwork([][2]int{
|
||||
[2]int{0, 1},
|
||||
[2]int{1, 2},
|
||||
[2]int{2, 3},
|
||||
[2]int{1, 3},
|
||||
[2]int{4, 5},
|
||||
}, nil)
|
||||
time.Sleep(10 * time.Millisecond) // Each node should know about all channels
|
||||
// There was bug in lnd when second search of the same path leads to lncli/lnd freeze
|
||||
for iter := 1; iter <= 3; iter++ {
|
||||
paths, err := nodes[0].FindKShortestPaths(nodes[3].Id, 2)
|
||||
if err != nil {
|
||||
t.Errorf("err = %v, want %v", err, nil)
|
||||
}
|
||||
correctPaths := [][]graph.Vertex{
|
||||
[]graph.Vertex{vertexFromInt(0), vertexFromInt(1), vertexFromInt(3)},
|
||||
[]graph.Vertex{vertexFromInt(0), vertexFromInt(1), vertexFromInt(2), vertexFromInt(3)},
|
||||
}
|
||||
if !reflect.DeepEqual(paths, correctPaths) {
|
||||
t.Errorf("on iteration: %v paths = %v, want %v", iter, paths, correctPaths)
|
||||
}
|
||||
}
|
||||
// Case when path do not exist
|
||||
paths, _ := nodes[0].FindKShortestPaths(vertexFromInt(7), 3)
|
||||
if len(paths) != 0 {
|
||||
t.Errorf("path = %v, want %v", paths, []graph.Vertex{})
|
||||
}
|
||||
net.Stop()
|
||||
}
|
83
routing/mocknet.go
Normal file
83
routing/mocknet.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
type MockNetwork struct {
|
||||
nodes map[graph.Vertex]*RoutingManager
|
||||
chMsg chan *RoutingMessage
|
||||
chQuit chan struct{}
|
||||
printMessages bool
|
||||
}
|
||||
|
||||
func NewMockNetwork(printMessages bool) *MockNetwork {
|
||||
return &MockNetwork{
|
||||
nodes: make(map[graph.Vertex]*RoutingManager),
|
||||
chMsg: make(chan *RoutingMessage),
|
||||
chQuit: make(chan struct{}),
|
||||
printMessages: printMessages,
|
||||
}
|
||||
}
|
||||
|
||||
func (net *MockNetwork) Start() {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case msg, ok := <-net.chMsg:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
receiverId := msg.ReceiverID
|
||||
// TODO: validate ReceiverID
|
||||
if net.printMessages {
|
||||
fmt.Println(msg.Msg)
|
||||
}
|
||||
if _, ok := net.nodes[receiverId]; ok {
|
||||
net.nodes[receiverId].ReceiveRoutingMessage(msg.Msg, msg.SenderID)
|
||||
}
|
||||
case <-net.chQuit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (net *MockNetwork) Stop() {
|
||||
close(net.chQuit)
|
||||
}
|
||||
|
||||
func (net *MockNetwork) Add(r *RoutingManager) {
|
||||
net.nodes[r.Id] = r
|
||||
chOut := make(chan *RoutingMessage)
|
||||
if r.config == nil {
|
||||
r.config = &RoutingConfig{}
|
||||
}
|
||||
r.config.SendMessage = func(receiver [33]byte, msg lnwire.Message) error {
|
||||
chOut <- &RoutingMessage{
|
||||
SenderID: r.Id,
|
||||
ReceiverID: graph.NewVertex(receiver[:]),
|
||||
Msg: msg,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case msg, ok := <-chOut:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
net.chMsg <- msg
|
||||
case <-net.chQuit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
67
routing/rt/graph/bfs.go
Normal file
67
routing/rt/graph/bfs.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import "container/list"
|
||||
|
||||
func ShortestPathLen(g *Graph, v1, v2 Vertex) (int, error) {
|
||||
dist, _, err := bfs(g, v1, v2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, ok := dist[v2]; !ok {
|
||||
return 0, PathNotFoundError
|
||||
}
|
||||
return dist[v2], nil
|
||||
}
|
||||
|
||||
func ShortestPath(g *Graph, v1, v2 Vertex) ([]Vertex, error) {
|
||||
_, parent, err := bfs(g, v1, v2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := parent[v2]; !ok {
|
||||
return nil, PathNotFoundError
|
||||
}
|
||||
path, err := Path(v1, v2, parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func bfs(g *Graph, v1, v2 Vertex) (map[Vertex]int, map[Vertex]Vertex, error) {
|
||||
var queue list.List
|
||||
queue.PushBack(v1)
|
||||
dist := make(map[Vertex]int)
|
||||
parent := make(map[Vertex]Vertex)
|
||||
dist[v1] = 0
|
||||
for queue.Len() != 0 {
|
||||
err := ibfs(g, queue.Front().Value.(Vertex), &queue, dist, parent)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if _, ok := dist[v2]; ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
return dist, parent, nil
|
||||
}
|
||||
|
||||
func ibfs(g *Graph, v Vertex, queue *list.List, dist map[Vertex]int, parent map[Vertex]Vertex) error {
|
||||
queue.Remove(queue.Front())
|
||||
targets, err := g.GetNeighbors(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for to := range targets {
|
||||
if _, ok := dist[to]; !ok {
|
||||
dist[to] = dist[v] + 1
|
||||
parent[to] = v
|
||||
queue.PushBack(to)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
31
routing/rt/graph/bfs_test.go
Normal file
31
routing/rt/graph/bfs_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShortestPath(t *testing.T) {
|
||||
g, v := newTestGraph()
|
||||
|
||||
tests := []struct{
|
||||
Source, Target Vertex
|
||||
ExpectedPath []Vertex
|
||||
}{
|
||||
{v[1], v[4], []Vertex{v[1], v[7], v[3], v[6], v[5], v[4]}},
|
||||
{v[5], v[7], []Vertex{v[5], v[6], v[3], v[7]}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
path, err := ShortestPath(g, test.Source, test.Target)
|
||||
if err != nil {
|
||||
t.Errorf("ShortestPath(g, %v, %v ) returns not nil error: %v", test.Source, test.Target, err)
|
||||
}
|
||||
if !reflect.DeepEqual(path, test.ExpectedPath) {
|
||||
t.Errorf("ShortestPath(g, %v, %v ) = %v, want %v", test.Source, test.Target, path, test.ExpectedPath)
|
||||
}
|
||||
}
|
||||
}
|
67
routing/rt/graph/dijkstra.go
Normal file
67
routing/rt/graph/dijkstra.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import "math"
|
||||
|
||||
func DijkstraPathWeight(g *Graph, source, target Vertex) (float64, error) {
|
||||
dist, _, err := dijkstra(g, source, target)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, ok := dist[target]; !ok {
|
||||
return 0, PathNotFoundError
|
||||
}
|
||||
return dist[target], nil
|
||||
}
|
||||
|
||||
func DijkstraPath(g *Graph, source, target Vertex) ([]Vertex, error) {
|
||||
_, parent, err := dijkstra(g, source, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := parent[target]; !ok {
|
||||
return nil, PathNotFoundError
|
||||
}
|
||||
path, err := Path(source, target, parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func dijkstra(g *Graph, source, target Vertex) (map[Vertex]float64, map[Vertex]Vertex, error) {
|
||||
dist := make(map[Vertex]float64)
|
||||
colored := make(map[Vertex]bool)
|
||||
parent := make(map[Vertex]Vertex)
|
||||
dist[source] = 0
|
||||
for {
|
||||
bestDist := math.MaxFloat64
|
||||
var bestID Vertex
|
||||
for id, val := range dist {
|
||||
if val < bestDist && !colored[id] {
|
||||
bestDist = val
|
||||
bestID = id
|
||||
}
|
||||
}
|
||||
if bestID == target {
|
||||
break
|
||||
}
|
||||
colored[bestID] = true
|
||||
targets, err := g.GetNeighbors(bestID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for id, multiedges := range targets {
|
||||
for _, edge := range multiedges {
|
||||
if have, ok := dist[id]; !ok || have > dist[bestID]+edge.Wgt {
|
||||
dist[id] = dist[bestID] + edge.Wgt
|
||||
parent[id] = bestID
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dist, parent, nil
|
||||
}
|
41
routing/rt/graph/dijkstra_test.go
Normal file
41
routing/rt/graph/dijkstra_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDijkstraPath(t *testing.T) {
|
||||
g, v := newTestGraph()
|
||||
|
||||
tests := []struct {
|
||||
Source, Target Vertex
|
||||
ExpectedPath []Vertex
|
||||
ExpectedWeight float64
|
||||
}{
|
||||
{v[1], v[4], []Vertex{v[1], v[7], v[2], v[3], v[6], v[5], v[4]}, 13},
|
||||
{v[5], v[7], []Vertex{v[5], v[6], v[3], v[2], v[7]}, 10},
|
||||
}
|
||||
for _, test := range tests {
|
||||
// Test DijkstraPath
|
||||
path, err := DijkstraPath(g, test.Source, test.Target)
|
||||
if err != nil {
|
||||
t.Errorf("DijkstraPath(g, %v, %v ) returns not nil error: %v", test.Source, test.Target, err)
|
||||
}
|
||||
if !reflect.DeepEqual(path, test.ExpectedPath) {
|
||||
t.Errorf("DijkstraPath(g, %v, %v ) = %v, want %v", test.Source, test.Target, path, test.ExpectedPath)
|
||||
}
|
||||
// Test DijkstraPathWeight
|
||||
weight, err := DijkstraPathWeight(g, test.Source, test.Target)
|
||||
if err != nil {
|
||||
t.Errorf("DijkstraPathWeight(g, %v, %v ) returns not nil error: %v", test.Source, test.Target, err)
|
||||
}
|
||||
if weight != test.ExpectedWeight {
|
||||
t.Errorf("DijkstraPathWeight(g, %v, %v ) = %v, want %v", test.Source, test.Target, weight, test.ExpectedWeight)
|
||||
}
|
||||
}
|
||||
}
|
357
routing/rt/graph/graph.go
Normal file
357
routing/rt/graph/graph.go
Normal file
@ -0,0 +1,357 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"bytes"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
// CyclicDataError represents internal error. When trying to
|
||||
// obtain explicit path after BFS. Following parents resulted
|
||||
// in a loop.
|
||||
CyclicDataError = errors.New("Cyclic data")
|
||||
|
||||
// PathNotFoundError represents not existing path
|
||||
// after search
|
||||
PathNotFoundError = errors.New("Path not found")
|
||||
|
||||
// NodeNotFoundError represents that required vertex
|
||||
// does not exist in the graph
|
||||
NodeNotFoundError = errors.New("Node not found")
|
||||
|
||||
// EdgeNotFoundError represents that requested edge
|
||||
// does not exist in the graph
|
||||
EdgeNotFoundError = errors.New("Edge not found")
|
||||
)
|
||||
|
||||
// Vertex represent graph vertex(node) in a
|
||||
// lightning network
|
||||
type Vertex struct {
|
||||
PubKey [33]byte
|
||||
}
|
||||
|
||||
// NilVertex represents not existing vertex.
|
||||
// This value should be used similar to nil
|
||||
var NilVertex = Vertex{PubKey: [33]byte{}}
|
||||
|
||||
// NewVertex creates a new vertex from a compressed public key.
|
||||
func NewVertex(v []byte) Vertex {
|
||||
x := Vertex{}
|
||||
copy(x.PubKey[:], v)
|
||||
return x
|
||||
}
|
||||
|
||||
// String creates a string representation of a Vertex.
|
||||
// Note: it does not hex encode.
|
||||
func (s Vertex) String() string {
|
||||
return string(s.PubKey[:])
|
||||
}
|
||||
|
||||
|
||||
// ToByte33 returns [33]byte
|
||||
// 33 - is usual byte length of a compressed
|
||||
// public key
|
||||
func (s Vertex) ToByte33() [33]byte {
|
||||
var rez [33]byte
|
||||
copy(rez[:], s.PubKey[:])
|
||||
return rez
|
||||
}
|
||||
|
||||
// ToByte() returns byte representation of
|
||||
// the vertex
|
||||
func (s Vertex) ToByte() []byte {
|
||||
return s.PubKey[:]
|
||||
}
|
||||
|
||||
// IsNil compares vertex to nil vertex
|
||||
func (s Vertex) IsNil() bool {
|
||||
var z [33]byte
|
||||
return bytes.Equal(s.PubKey[:], z[:])
|
||||
}
|
||||
|
||||
// EdgeID represent edge unique identifier.
|
||||
type EdgeID wire.OutPoint
|
||||
|
||||
// NilEdgeID represent not existing EdgeID
|
||||
var NilEdgeID = EdgeID{wire.ShaHash{}, 0}
|
||||
|
||||
// NewEdgeID returns new EdgeID
|
||||
func NewEdgeID(v wire.OutPoint) EdgeID {
|
||||
return EdgeID(v)
|
||||
}
|
||||
|
||||
// String returns string representation of EdgeID
|
||||
func (e EdgeID) String() string {
|
||||
return wire.OutPoint(e).String()
|
||||
}
|
||||
|
||||
// ChannelInfo contains information about edge(channel)
|
||||
// like capacity or weight
|
||||
type ChannelInfo struct {
|
||||
// Capacity in satoshi of the channel
|
||||
Cpt int64
|
||||
// Weight of the channel
|
||||
Wgt float64
|
||||
}
|
||||
|
||||
// Copy() creates a copy of a ChannelInfo struct.
|
||||
// If c==nil than it returns nil.
|
||||
// This method is used to safely create copy of a structure
|
||||
// given by a pointer which may be nil.
|
||||
func (c *ChannelInfo) Copy() *ChannelInfo {
|
||||
if c == nil {
|
||||
return nil
|
||||
} else {
|
||||
c1 := *c
|
||||
return &c1
|
||||
}
|
||||
}
|
||||
|
||||
// Edge represents edge in a graph
|
||||
type Edge struct {
|
||||
// Source and Target
|
||||
Src, Tgt Vertex
|
||||
|
||||
// Edge identifier
|
||||
Id EdgeID
|
||||
|
||||
// Additional information about edge
|
||||
Info *ChannelInfo
|
||||
}
|
||||
|
||||
// NilEdge represents nil (not-existing) edge
|
||||
var NilEdge = Edge{NilVertex, NilVertex, NilEdgeID, nil}
|
||||
|
||||
// String returns string of an Edge
|
||||
func (e Edge) String() string {
|
||||
return fmt.Sprintf("edge[%v %v %v %v]", e.Src, e.Tgt, e.Id, e.Info)
|
||||
}
|
||||
|
||||
// NewEdge create a new edge
|
||||
func NewEdge(src, tgt Vertex, id EdgeID, info *ChannelInfo) Edge {
|
||||
return Edge{
|
||||
Src: src,
|
||||
Tgt: tgt,
|
||||
Id: id,
|
||||
Info: info,
|
||||
}
|
||||
}
|
||||
|
||||
// Graph is multigraph implementation.
|
||||
type Graph struct {
|
||||
adjacencyList map[Vertex]map[Vertex]map[EdgeID]*ChannelInfo
|
||||
}
|
||||
|
||||
// NewGraph creates a new empty graph.
|
||||
func NewGraph() *Graph {
|
||||
g := new(Graph)
|
||||
g.adjacencyList = make(map[Vertex]map[Vertex]map[EdgeID]*ChannelInfo)
|
||||
return g
|
||||
}
|
||||
|
||||
// GetVertexCount returns number of vertexes in a graph.
|
||||
func (g *Graph) GetVertexCount() int {
|
||||
return len(g.adjacencyList)
|
||||
}
|
||||
|
||||
// GetVertexes returns all vertexes in a graph.
|
||||
func (g *Graph) GetVertexes() []Vertex {
|
||||
IDs := make([]Vertex, 0, g.GetVertexCount())
|
||||
for ID := range g.adjacencyList {
|
||||
IDs = append(IDs, ID)
|
||||
}
|
||||
return IDs
|
||||
}
|
||||
|
||||
// GetEdges return all edges in a graph.
|
||||
// For undirected graph it returns each edge twice (for each direction)
|
||||
// To get all edges in undirected graph use GetUndirectedEdges
|
||||
func (g *Graph) GetEdges() []Edge {
|
||||
edges := make([]Edge, 0)
|
||||
for v1 := range g.adjacencyList {
|
||||
for v2, multiedges := range g.adjacencyList[v1] {
|
||||
for id, edge := range multiedges {
|
||||
edges = append(edges, NewEdge(v1, v2, id, edge))
|
||||
}
|
||||
}
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// GetUndirectedEdges returns all edges in an undirected graph.
|
||||
func (g *Graph) GetUndirectedEdges() []Edge {
|
||||
edges := make([]Edge, 0)
|
||||
for v1 := range g.adjacencyList {
|
||||
for v2, multiedges := range g.adjacencyList[v1] {
|
||||
if v1.String() <= v2.String() {
|
||||
for id, edge := range multiedges {
|
||||
edges = append(edges, NewEdge(v1, v2, id, edge))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return edges
|
||||
}
|
||||
|
||||
// HasVertex check if graph contain the given vertex.
|
||||
func (g *Graph) HasVertex(v Vertex) bool {
|
||||
_, ok := g.adjacencyList[v]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasEdge check if there is edge with a given EdgeID
|
||||
// between two given vertexes.
|
||||
func (g *Graph) HasEdge(v1, v2 Vertex, edgeID EdgeID) bool {
|
||||
if _, ok := g.adjacencyList[v1]; ok {
|
||||
if multiedges, ok := g.adjacencyList[v1][v2]; ok {
|
||||
if _, ok := multiedges[edgeID]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddVertex adds vertex to a graph.
|
||||
// If graph already contains this vertex it does nothing.
|
||||
// Returns true if vertex previously doesnt't exist.
|
||||
func (g *Graph) AddVertex(v Vertex) bool {
|
||||
if g.HasVertex(v) {
|
||||
return false
|
||||
}
|
||||
g.adjacencyList[v] = make(map[Vertex]map[EdgeID]*ChannelInfo)
|
||||
return true
|
||||
}
|
||||
|
||||
// RemoveVertex removes vertex from a graph
|
||||
// If a graph already does not contain this vertex it does nothing
|
||||
// Returns true if previously existed vertex got deleted
|
||||
// BUG(mkl): does it correctly deletes edges with this vertex
|
||||
func (g *Graph) RemoveVertex(v Vertex) bool {
|
||||
if !g.HasVertex(v) {
|
||||
return false
|
||||
}
|
||||
delete(g.adjacencyList, v)
|
||||
return true
|
||||
}
|
||||
|
||||
// AddEdge adds directed edge to the graph
|
||||
// v1, v2 must exist. If they do not exist this function do nothing
|
||||
// and return false. If edge with given vertexes and id already exists it
|
||||
// gets overwritten
|
||||
func (g *Graph) AddEdge(v1, v2 Vertex, edgeID EdgeID, info *ChannelInfo) bool {
|
||||
if !g.HasVertex(v1) || !g.HasVertex(v2) {
|
||||
return false
|
||||
}
|
||||
tmap := g.adjacencyList[v1]
|
||||
if tmap[v2] == nil {
|
||||
tmap[v2] = make(map[EdgeID]*ChannelInfo)
|
||||
}
|
||||
tmap[v2][edgeID] = info
|
||||
return true
|
||||
}
|
||||
|
||||
// AddUndirectedEdge adds an undirected edge to the graph.
|
||||
// Vertexes should exists.
|
||||
func (g *Graph) AddUndirectedEdge(v1, v2 Vertex, edgeID EdgeID, info *ChannelInfo) bool {
|
||||
ok1 := g.AddEdge(v1, v2, edgeID, info)
|
||||
ok2 := g.AddEdge(v2, v1, edgeID, info)
|
||||
return ok1 && ok2
|
||||
}
|
||||
|
||||
// ReplaceEdge replaces directed edge in the graph
|
||||
func (g *Graph) ReplaceEdge(v1, v2 Vertex, edgeID EdgeID, info *ChannelInfo) bool {
|
||||
if tmap, ok := g.adjacencyList[v1]; ok {
|
||||
if _, ok := tmap[v2]; ok {
|
||||
if _, ok := tmap[v2][edgeID]; ok {
|
||||
tmap[v2][edgeID] = info
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReplaceUndirectedEdge replaces undirected edge in the graph.
|
||||
func (g *Graph) ReplaceUndirectedEdge(v1, v2 Vertex, edgeID EdgeID, info *ChannelInfo) bool {
|
||||
ok1 := g.ReplaceEdge(v1, v2, edgeID, info)
|
||||
ok2 := g.ReplaceEdge(v2, v1, edgeID, info)
|
||||
return ok1 && ok2
|
||||
}
|
||||
|
||||
// RemoveEdge removes directed edge in a graph.
|
||||
func (g *Graph) RemoveEdge(v1, v2 Vertex, edgeID EdgeID) bool {
|
||||
if _, ok := g.adjacencyList[v1]; ok {
|
||||
if _, ok := g.adjacencyList[v1][v2]; ok {
|
||||
tmap := g.adjacencyList[v1][v2]
|
||||
if _, ok := tmap[edgeID]; ok {
|
||||
delete(tmap, edgeID)
|
||||
if len(tmap) == 0 {
|
||||
delete(g.adjacencyList[v1], v2)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveUndirectedEdge removes undirected edge in a graph.
|
||||
func (g *Graph) RemoveUndirectedEdge(v1, v2 Vertex, edgeID EdgeID) bool {
|
||||
ok1 := g.RemoveEdge(v1, v2, edgeID)
|
||||
ok2 := g.RemoveEdge(v2, v1, edgeID)
|
||||
return ok1 && ok2
|
||||
}
|
||||
|
||||
// GetInfo returns info about edge in a graph.
|
||||
func (g *Graph) GetInfo(v1, v2 Vertex, edgeID EdgeID) (*ChannelInfo, error) {
|
||||
if tmap, ok := g.adjacencyList[v1]; ok {
|
||||
if _, ok := tmap[v2]; ok {
|
||||
if _, ok := tmap[v2][edgeID]; ok {
|
||||
return tmap[v2][edgeID], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, EdgeNotFoundError
|
||||
}
|
||||
|
||||
// GetNeighbors returns neighbors of a given vertex in a graph.
|
||||
// Note: output should not be modified because it will change
|
||||
// original graph
|
||||
func (g *Graph) GetNeighbors(v Vertex) (map[Vertex]map[EdgeID]*ChannelInfo, error) {
|
||||
if !g.HasVertex(v) {
|
||||
return nil, NodeNotFoundError
|
||||
}
|
||||
return g.adjacencyList[v], nil
|
||||
}
|
||||
|
||||
// MinCostChannel return channel with minimal weight between two vertexes
|
||||
func (g *Graph) MinCostChannel(v1, v2 Vertex) (*ChannelInfo, error) {
|
||||
if !g.HasVertex(v1) || !g.HasVertex(v2) {
|
||||
return nil, NodeNotFoundError
|
||||
}
|
||||
if _, ok := g.adjacencyList[v1][v2]; !ok {
|
||||
return nil, EdgeNotFoundError
|
||||
}
|
||||
wgt := math.MaxFloat64
|
||||
var easiest *ChannelInfo
|
||||
for _, edge := range g.adjacencyList[v1][v2] {
|
||||
if edge.Wgt < wgt {
|
||||
wgt, easiest = edge.Wgt, edge
|
||||
}
|
||||
}
|
||||
return easiest, nil
|
||||
}
|
||||
|
||||
|
||||
// Bfs do breadth-first search starting from a given vertex
|
||||
func (g *Graph) Bfs(source Vertex) (map[Vertex]int, map[Vertex]Vertex, error) {
|
||||
return bfs(g, source, NilVertex)
|
||||
}
|
166
routing/rt/graph/graph_test.go
Normal file
166
routing/rt/graph/graph_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func vertexFromInt(x int) Vertex {
|
||||
s := fmt.Sprintf("%v", x)
|
||||
return NewVertex([]byte(s))
|
||||
}
|
||||
|
||||
func edgeIdFromString(s string) EdgeID {
|
||||
e := EdgeID{}
|
||||
copy(e.Hash[:], []byte(s))
|
||||
return e
|
||||
}
|
||||
|
||||
// newTestGraph returns new graph for testing purposes
|
||||
// Each time it returns new graph
|
||||
func newTestGraph() (*Graph, []Vertex) {
|
||||
g := NewGraph()
|
||||
v := make([]Vertex, 8)
|
||||
for i:=1; i<8; i++ {
|
||||
v[i] = vertexFromInt(i)
|
||||
g.AddVertex(v[i])
|
||||
}
|
||||
edges := []Edge{
|
||||
{v[1], v[7], edgeIdFromString("2"), &ChannelInfo{Wgt:2}},
|
||||
{v[7], v[2], edgeIdFromString("1"), &ChannelInfo{Wgt:1}},
|
||||
{v[3], v[7], edgeIdFromString("10"), &ChannelInfo{Wgt:10}},
|
||||
{v[2], v[3], edgeIdFromString("2"), &ChannelInfo{Wgt:2}},
|
||||
{v[3], v[6], edgeIdFromString("4"), &ChannelInfo{Wgt:4}},
|
||||
{v[5], v[6], edgeIdFromString("3"), &ChannelInfo{Wgt:3}},
|
||||
{v[4], v[5], edgeIdFromString("1"), &ChannelInfo{Wgt:1}},
|
||||
}
|
||||
for _, e := range edges {
|
||||
g.AddUndirectedEdge(e.Src, e.Tgt, e.Id, e.Info)
|
||||
}
|
||||
return g, v
|
||||
}
|
||||
|
||||
func TestNodeManipulation(t *testing.T) {
|
||||
g := NewGraph()
|
||||
if g.HasVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if !g.AddVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if g.AddVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if !g.HasVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if !g.RemoveVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if g.RemoveVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if g.HasVertex(vertexFromInt(2)) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEdgeManipulation(t *testing.T) {
|
||||
g := NewGraph()
|
||||
if g.AddEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
g.AddVertex(vertexFromInt(2))
|
||||
g.AddVertex(vertexFromInt(3))
|
||||
if g.HasEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if g.ReplaceEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, &ChannelInfo{1, 2}) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if !g.AddEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if !g.AddEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if !g.HasEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if !g.ReplaceEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, &ChannelInfo{1, 2}) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if info, err := g.GetInfo(vertexFromInt(2), vertexFromInt(3), NilEdgeID); err != nil {
|
||||
panic(err)
|
||||
} else if !reflect.DeepEqual(*info, ChannelInfo{1, 2}) {
|
||||
t.Errorf("expected: %v, actual: %v", ChannelInfo{1, 2}, *info)
|
||||
}
|
||||
if !g.RemoveEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID) {
|
||||
t.Errorf("expected: %t, actual: %t", true, false)
|
||||
}
|
||||
if g.RemoveEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
if g.HasEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID) {
|
||||
t.Errorf("expected: %t, actual: %t", false, true)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllGetMethods(t *testing.T) {
|
||||
g := NewGraph()
|
||||
g.AddVertex(vertexFromInt(2))
|
||||
g.AddVertex(vertexFromInt(3))
|
||||
if vertexCount := g.GetVertexCount(); vertexCount != 2 {
|
||||
t.Errorf("expected: %d, actual: %d", 2, vertexCount)
|
||||
}
|
||||
if vs := g.GetVertexes(); !reflect.DeepEqual(vs, []Vertex{vertexFromInt(2), vertexFromInt(3)}) &&
|
||||
!reflect.DeepEqual(vs, []Vertex{vertexFromInt(3), vertexFromInt(2)}) {
|
||||
t.Errorf("expected: %v, actual: %v",
|
||||
[]Vertex{vertexFromInt(2), vertexFromInt(3)},
|
||||
vs,
|
||||
)
|
||||
}
|
||||
g.AddEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil)
|
||||
if edges := g.GetEdges(); !reflect.DeepEqual(edges, []Edge{NewEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil)}) {
|
||||
t.Errorf("expected: %v, actual: %v",
|
||||
[]Edge{NewEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, nil)},
|
||||
edges,
|
||||
)
|
||||
}
|
||||
|
||||
if targets, err := g.GetNeighbors(vertexFromInt(2)); err != nil {
|
||||
panic(err)
|
||||
} else if !reflect.DeepEqual(targets, map[Vertex]map[EdgeID]*ChannelInfo{vertexFromInt(3): map[EdgeID]*ChannelInfo{NilEdgeID: nil}}) {
|
||||
t.Errorf("expected: %v, actual: %v",
|
||||
map[Vertex]map[EdgeID]*ChannelInfo{vertexFromInt(3): map[EdgeID]*ChannelInfo{NilEdgeID: nil}},
|
||||
targets,
|
||||
)
|
||||
}
|
||||
|
||||
g2 := NewGraph()
|
||||
g2.AddVertex(vertexFromInt(2))
|
||||
g2.AddVertex(vertexFromInt(3))
|
||||
g2.AddEdge(vertexFromInt(2), vertexFromInt(3), NilEdgeID, &ChannelInfo{Wgt: 42})
|
||||
if info, err := g2.GetInfo(vertexFromInt(2), vertexFromInt(3), NilEdgeID); err != nil {
|
||||
panic(err)
|
||||
} else if wgt := info.Wgt; wgt != 42 {
|
||||
t.Errorf("expected: %v, actual: %v", wgt, 42)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVertexToByte33(t *testing.T) {
|
||||
var b1 [33]byte
|
||||
for i := 0; i < 33; i++ {
|
||||
b1[i] = byte(i)
|
||||
}
|
||||
v := NewVertex(b1[:])
|
||||
b2 := v.ToByte33()
|
||||
if b1 != b2 {
|
||||
t.Errorf("Wrong result of ID.ToByte33()= %v, want %v", b2, b1)
|
||||
}
|
||||
}
|
68
routing/rt/graph/ksp.go
Normal file
68
routing/rt/graph/ksp.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import "math"
|
||||
|
||||
// KShortestPaths finds k shortest paths
|
||||
// Note: this implementation finds k path not necessary shortest
|
||||
// It tries to make that distinct and shortest at the same time
|
||||
func KShortestPaths(g *Graph, source, target Vertex, k int) ([][]Vertex, error) {
|
||||
ksp := make([][]Vertex, 0, k)
|
||||
DRY := make(map[string]bool)
|
||||
actualNodeWeight := make(map[Vertex]float64, g.GetVertexCount())
|
||||
for _, id := range g.GetVertexes() {
|
||||
actualNodeWeight[id] = 1
|
||||
}
|
||||
const UselessIterations = 200
|
||||
for cnt := 0; len(ksp) < k && cnt < UselessIterations; cnt++ {
|
||||
if err := modifyEdgeWeight(g, actualNodeWeight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := DijkstraPath(g, source, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range path {
|
||||
actualNodeWeight[v]++
|
||||
}
|
||||
key := ""
|
||||
for _, v := range path {
|
||||
key += v.String()
|
||||
}
|
||||
if !DRY[key] {
|
||||
DRY[key] = true
|
||||
ksp = append(ksp, path)
|
||||
cnt = 0
|
||||
}
|
||||
}
|
||||
return ksp, nil
|
||||
}
|
||||
|
||||
func modifyEdgeWeight(g *Graph, actualNodeWeight map[Vertex]float64) error {
|
||||
for _, v1 := range g.GetVertexes() {
|
||||
targets, err := g.GetNeighbors(v1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for v2, multiedges := range targets {
|
||||
for ID := range multiedges {
|
||||
wgt := calcEdgeWeight(actualNodeWeight, v1, v2)
|
||||
g.ReplaceUndirectedEdge(v1, v2, ID, &ChannelInfo{Wgt: wgt})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Calculate new edge weight based on vertex weights.
|
||||
// It uses empirical formulae
|
||||
// weight(i, j) = (weight(i) + weight(j)) ^ 6
|
||||
// Number 6 was choosen because it gives best results in several simulations
|
||||
func calcEdgeWeight(actualNodeWeight map[Vertex]float64, v1, v2 Vertex) float64 {
|
||||
const ExperiementalNumber = 6.0
|
||||
wgt := math.Pow(actualNodeWeight[v1]+actualNodeWeight[v2], ExperiementalNumber)
|
||||
return wgt
|
||||
}
|
39
routing/rt/graph/ksp_test.go
Normal file
39
routing/rt/graph/ksp_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKSP(t *testing.T) {
|
||||
g, v := newTestGraph()
|
||||
|
||||
tests := []struct {
|
||||
Source, Target Vertex
|
||||
K int
|
||||
ExpectedPaths [][]Vertex
|
||||
}{
|
||||
{v[1], v[4], 10, [][]Vertex{
|
||||
{v[1], v[7], v[3], v[6], v[5], v[4]},
|
||||
{v[1], v[7], v[2], v[3], v[6], v[5], v[4]},
|
||||
},
|
||||
},
|
||||
{v[5], v[7], 1, [][]Vertex{
|
||||
{v[5], v[6], v[3], v[7]},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
paths, err := KShortestPaths(g, test.Source, test.Target, test.K)
|
||||
if err != nil {
|
||||
t.Errorf("KShortestPaths(g, %v, %v, %v) returns not nil error: %v", test.Source, test.Target, test.K, err)
|
||||
}
|
||||
if !reflect.DeepEqual(paths, test.ExpectedPaths) {
|
||||
t.Errorf("KShortestPaths(g, %v, %v, %v) = %v, want %v", test.Source, test.Target, test.K, paths, test.ExpectedPaths)
|
||||
}
|
||||
}
|
||||
}
|
39
routing/rt/graph/path.go
Normal file
39
routing/rt/graph/path.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package graph
|
||||
|
||||
import "errors"
|
||||
|
||||
func Path(v1, v2 Vertex, parent map[Vertex]Vertex) ([]Vertex, error) {
|
||||
path, err := ReversePath(v1, v2, parent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
Reverse(path)
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func ReversePath(v1, v2 Vertex, parent map[Vertex]Vertex) ([]Vertex, error) {
|
||||
path := []Vertex{v2}
|
||||
for v2 != v1 {
|
||||
if v2 == parent[v2] {
|
||||
return nil, CyclicDataError
|
||||
}
|
||||
var ok bool
|
||||
v2, ok = parent[v2]
|
||||
if !ok {
|
||||
return nil, errors.New("Invalid key")
|
||||
}
|
||||
path = append(path, v2)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func Reverse(path []Vertex) {
|
||||
length := len(path)
|
||||
for i := 0; i < length/2; i++ {
|
||||
path[i], path[length-1-i] = path[length-1-i], path[i]
|
||||
}
|
||||
}
|
258
routing/rt/rt.go
Normal file
258
routing/rt/rt.go
Normal file
@ -0,0 +1,258 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package rt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
)
|
||||
|
||||
type OperationType byte
|
||||
const (
|
||||
AddChannelOP OperationType = iota
|
||||
RemoveChannelOp
|
||||
)
|
||||
|
||||
// String returns string representation
|
||||
func (t OperationType) String()string {
|
||||
switch t {
|
||||
case AddChannelOP:
|
||||
return "<ADD_CHANNEL>"
|
||||
case RemoveChannelOp:
|
||||
return "<REMOVE_CHANNEL>"
|
||||
default:
|
||||
return "<UNKNOWN_TYPE>"
|
||||
}
|
||||
}
|
||||
|
||||
// ChannelOperation represent operation on a graph
|
||||
// Such as add edge or remove edge
|
||||
type ChannelOperation struct {
|
||||
graph.Edge
|
||||
Operation OperationType // One of ADD_CHANNEL, REMOVE_CHANNEL
|
||||
}
|
||||
|
||||
// NewChannelOperation returns new ChannelOperation
|
||||
func NewChannelOperation(src, tgt graph.Vertex, id graph.EdgeID, info *graph.ChannelInfo, opType OperationType) ChannelOperation{
|
||||
return ChannelOperation{
|
||||
Edge: graph.Edge{
|
||||
Src: src,
|
||||
Tgt: tgt,
|
||||
Id: id,
|
||||
Info: info.Copy(),
|
||||
},
|
||||
Operation: opType,
|
||||
}
|
||||
}
|
||||
|
||||
// String return string representation
|
||||
func (c ChannelOperation) String () string {
|
||||
return fmt.Sprintf(
|
||||
"ChannelOperation[%v %v %v %v %v]",
|
||||
c.Src,
|
||||
c.Tgt,
|
||||
c.Id,
|
||||
c.Info,
|
||||
c.Operation,
|
||||
)
|
||||
}
|
||||
|
||||
// Copy returns copy of ChannelOperation
|
||||
func (op *ChannelOperation) Copy() ChannelOperation {
|
||||
return ChannelOperation{
|
||||
Edge: graph.Edge{
|
||||
Src: op.Src,
|
||||
Tgt: op.Tgt,
|
||||
Id: op.Id,
|
||||
Info: op.Info.Copy(),
|
||||
},
|
||||
Operation: op.Operation,
|
||||
}
|
||||
}
|
||||
|
||||
// DifferenceBuffer represent multiple changes in a graph
|
||||
// such as adding or deleting edges
|
||||
type DifferenceBuffer []ChannelOperation
|
||||
|
||||
// String returns string representation
|
||||
func (d DifferenceBuffer) String() string {
|
||||
s := ""
|
||||
for i:=0; i<len(d); i++ {
|
||||
s += fmt.Sprintf(" %v\n", d[i])
|
||||
}
|
||||
s1 := ""
|
||||
if len(d) > 0 {
|
||||
s1 = "\n"
|
||||
}
|
||||
return fmt.Sprintf("DifferenceBuffer[%v%v]", s1, s)
|
||||
}
|
||||
|
||||
// NewDifferenceBuffer create new empty DifferenceBuffer
|
||||
func NewDifferenceBuffer() *DifferenceBuffer {
|
||||
d := make(DifferenceBuffer, 0)
|
||||
return &d
|
||||
}
|
||||
|
||||
// IsEmpty checks if buffer is empty(contains no operations in it)
|
||||
func (diffBuff *DifferenceBuffer) IsEmpty() bool {
|
||||
return len(*diffBuff) == 0
|
||||
}
|
||||
|
||||
// Clear deletes all operations from DifferenceBuffer making it empty
|
||||
func (diffBuff *DifferenceBuffer) Clear() {
|
||||
*diffBuff = make([]ChannelOperation, 0)
|
||||
}
|
||||
|
||||
// Copy create copy of a DifferenceBuffer
|
||||
func (diffBuff *DifferenceBuffer) Copy() *DifferenceBuffer {
|
||||
d := make([]ChannelOperation, 0, len(*diffBuff))
|
||||
for _, op := range *diffBuff {
|
||||
d = append(d, op.Copy())
|
||||
}
|
||||
return (*DifferenceBuffer)(&d)
|
||||
}
|
||||
|
||||
// RoutingTable represent information about graph and neighbors
|
||||
// TODO(mkl): better doc
|
||||
// Methods of this struct is not thread safe
|
||||
type RoutingTable struct {
|
||||
// Contains node's view of lightning network
|
||||
G *graph.Graph
|
||||
// Contains changes to send to a different neighbors
|
||||
// Changing RT modifies DifferenceBuffer
|
||||
diff []*DifferenceBuffer
|
||||
}
|
||||
|
||||
// ShortestPath find shortest path between two node in routing table
|
||||
func (rt *RoutingTable) ShortestPath(src, dst graph.Vertex) ([]graph.Vertex, error) {
|
||||
return graph.ShortestPath(rt.G, src, dst)
|
||||
}
|
||||
|
||||
// NewRoutingTable creates new empty routing table
|
||||
func NewRoutingTable() *RoutingTable {
|
||||
return &RoutingTable{
|
||||
G: graph.NewGraph(),
|
||||
diff: make([]*DifferenceBuffer, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// AddNodes add multiple nodes to the routing table
|
||||
func (rt *RoutingTable) AddNodes(IDs ...graph.Vertex) {
|
||||
for _, ID := range IDs {
|
||||
rt.G.AddVertex(ID)
|
||||
}
|
||||
}
|
||||
|
||||
// AddChannel adds channel to the routing table.
|
||||
// It will add nodes if they are not present in RT.
|
||||
// It will update all difference buffers
|
||||
func (rt *RoutingTable) AddChannel(Node1, Node2 graph.Vertex, edgeID graph.EdgeID, info *graph.ChannelInfo) {
|
||||
rt.AddNodes(Node1, Node2)
|
||||
if !rt.HasChannel(Node1, Node2, edgeID) {
|
||||
rt.G.AddUndirectedEdge(Node1, Node2, edgeID, info)
|
||||
for _, diffBuff := range rt.diff {
|
||||
*diffBuff = append(*diffBuff, NewChannelOperation(Node1, Node2, edgeID, info, AddChannelOP))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HasChannel check if channel between two nodes exist in routing table
|
||||
func (rt *RoutingTable) HasChannel(node1, node2 graph.Vertex, edgeID graph.EdgeID) bool {
|
||||
return rt.G.HasEdge(node1, node2, edgeID)
|
||||
}
|
||||
|
||||
// RemoveChannel removes channel from the routing table.
|
||||
// It will do nothing if channel does not exist in RT.
|
||||
// It will update all difference buffers
|
||||
func (rt *RoutingTable) RemoveChannel(Node1, Node2 graph.Vertex, edgeID graph.EdgeID) {
|
||||
if rt.HasChannel(Node1, Node2, edgeID) {
|
||||
rt.G.RemoveUndirectedEdge(Node1, Node2, edgeID)
|
||||
for _, diffBuff := range rt.diff {
|
||||
*diffBuff = append(*diffBuff, NewChannelOperation(Node1, Node2, edgeID, nil, RemoveChannelOp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewDiffBuff create a new difference buffer in a routing table
|
||||
func (rt *RoutingTable) NewDiffBuff() *DifferenceBuffer {
|
||||
buff := NewDifferenceBuffer()
|
||||
rt.diff = append(rt.diff, buff)
|
||||
return buff
|
||||
}
|
||||
|
||||
// ApplyDiffBuff applies difference buffer to the routing table.
|
||||
// It will modify RoutingTable's difference buffers.
|
||||
func (rt *RoutingTable) ApplyDiffBuff(diffBuff *DifferenceBuffer) {
|
||||
for _, op := range *diffBuff {
|
||||
if op.Operation == AddChannelOP {
|
||||
rt.AddChannel(op.Src, op.Tgt, op.Id, op.Info)
|
||||
} else if op.Operation == RemoveChannelOp {
|
||||
rt.RemoveChannel(op.Src, op.Tgt, op.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes return all nodes from routing table
|
||||
func (rt *RoutingTable) Nodes() []graph.Vertex {
|
||||
return rt.G.GetVertexes()
|
||||
}
|
||||
|
||||
// NumberOfNodes returns number of nodes in routing table
|
||||
func (rt *RoutingTable) NumberOfNodes() int {
|
||||
return rt.G.GetVertexCount()
|
||||
}
|
||||
|
||||
// AllChannels returns all channels from routing table
|
||||
func (rt *RoutingTable) AllChannels() []graph.Edge {
|
||||
edges := rt.G.GetUndirectedEdges()
|
||||
return edges
|
||||
}
|
||||
|
||||
// AddTable adds other RoutingTable.
|
||||
// Resulting table contains channels, nodes from both tables.
|
||||
func (rt *RoutingTable) AddTable(rt1 *RoutingTable) {
|
||||
newChannels := rt1.AllChannels()
|
||||
for _, channel := range newChannels {
|
||||
rt.AddChannel(channel.Src, channel.Tgt, channel.Id, channel.Info.Copy())
|
||||
}
|
||||
}
|
||||
|
||||
// Copy - creates a copy of RoutingTable
|
||||
// It makes deep copy of routing table.
|
||||
// Note: difference buffers are not copied!
|
||||
func (rt *RoutingTable) Copy() *RoutingTable {
|
||||
// TODO: add tests
|
||||
channels := rt.AllChannels()
|
||||
newRT := NewRoutingTable()
|
||||
newRT.AddNodes(rt.Nodes()...)
|
||||
for _, channel := range channels {
|
||||
newRT.AddChannel(channel.Src, channel.Tgt, channel.Id, channel.Info.Copy())
|
||||
}
|
||||
return newRT
|
||||
}
|
||||
|
||||
// String returns string representation of routing table
|
||||
func (rt *RoutingTable) String() string {
|
||||
rez := ""
|
||||
edges := rt.G.GetUndirectedEdges()
|
||||
for _, edge := range edges {
|
||||
rez += fmt.Sprintf("%v %v %v", edge.Src, edge.Tgt, edge.Info)
|
||||
}
|
||||
return rez
|
||||
}
|
||||
|
||||
// Find k-shortest path between source and destination
|
||||
func (rt *RoutingTable) KShortestPaths(src, dst graph.Vertex, k int) ([][]graph.Vertex, error) {
|
||||
return graph.KShortestPaths(rt.G, src, dst, k)
|
||||
}
|
||||
|
||||
// Returns copy of channel info for channel in routing table
|
||||
func (rt *RoutingTable) GetChannelInfo(id1, id2 graph.Vertex, edgeId graph.EdgeID) (*graph.ChannelInfo, error) {
|
||||
info, err := rt.G.GetInfo(id1, id2, edgeId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info.Copy(), nil
|
||||
}
|
160
routing/rt/rt_test.go
Normal file
160
routing/rt/rt_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package rt
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
NewID = vertexFromString
|
||||
)
|
||||
|
||||
func vertexFromString(s string) graph.Vertex {
|
||||
return graph.NewVertex([]byte(s))
|
||||
}
|
||||
|
||||
func edgeIdFromString(s string) graph.EdgeID {
|
||||
e := graph.EdgeID{}
|
||||
copy(e.Hash[:], []byte(s))
|
||||
return e
|
||||
}
|
||||
|
||||
func TestRT(t *testing.T) {
|
||||
r := NewRoutingTable()
|
||||
r.AddNodes(vertexFromString("1"), vertexFromString("2"))
|
||||
|
||||
r.AddChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID"), nil)
|
||||
if !r.HasChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(1, 2) == false, want true`)
|
||||
}
|
||||
if !r.HasChannel(vertexFromString("2"), vertexFromString("1"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(2, 1) == false, want true`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
r := NewRoutingTable()
|
||||
buff := r.NewDiffBuff()
|
||||
// Add 2 channels
|
||||
r.AddNodes(vertexFromString("1"), vertexFromString("2"), vertexFromString("3"))
|
||||
|
||||
r.AddChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID"), nil)
|
||||
r.AddChannel(vertexFromString("2"), vertexFromString("3"), edgeIdFromString("EdgeID"), nil)
|
||||
r1 := NewRoutingTable()
|
||||
r1.ApplyDiffBuff(buff)
|
||||
if !r.HasChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(1, 2) == false, want true`)
|
||||
}
|
||||
if !r.HasChannel(vertexFromString("2"), vertexFromString("3"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(2, 3) == false, want true`)
|
||||
}
|
||||
// Remove channel
|
||||
r.RemoveChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID"))
|
||||
r1.ApplyDiffBuff(buff)
|
||||
if r.HasChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(1, 2) == true, want false`)
|
||||
}
|
||||
if r.HasChannel(vertexFromString("2"), vertexFromString("1"), edgeIdFromString("EdgeID")) {
|
||||
t.Error(`r.HasChannel(2, 1) == true, want false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTChannels(t *testing.T) {
|
||||
rt := NewRoutingTable()
|
||||
rt.AddNodes(vertexFromString("1"), vertexFromString("2"), vertexFromString("3"))
|
||||
|
||||
rt.AddChannel(vertexFromString("1"), vertexFromString("2"), edgeIdFromString("EdgeID"), nil)
|
||||
rt.AddChannel(NewID("2"), NewID("3"), edgeIdFromString("EdgeID"), nil)
|
||||
|
||||
channels := rt.AllChannels()
|
||||
if len(channels) != 2 {
|
||||
t.Errorf(`rt.AllChannels == %v, want %v`, len(channels), 2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTAddTable(t *testing.T) {
|
||||
rt1 := NewRoutingTable()
|
||||
rt1.AddNodes(NewID("0"), NewID("1"), NewID("2"), NewID("3"), NewID("5"), NewID("6"))
|
||||
|
||||
rt1.AddChannel(NewID("0"), NewID("1"), edgeIdFromString("EdgeID"), nil)
|
||||
rt1.AddChannel(NewID("1"), NewID("2"), edgeIdFromString("EdgeID"), nil)
|
||||
|
||||
rt2 := NewRoutingTable()
|
||||
rt2.AddNodes(NewID("0"), NewID("1"), NewID("2"), NewID("3"), NewID("5"), NewID("6"))
|
||||
|
||||
rt2.AddChannel(NewID("2"), NewID("3"), edgeIdFromString("EdgeID"), nil)
|
||||
rt2.AddChannel(NewID("5"), NewID("6"), edgeIdFromString("EdgeID"), nil)
|
||||
rt1.AddTable(rt2)
|
||||
expectedChannels := [][]graph.Vertex{
|
||||
[]graph.Vertex{NewID("0"), NewID("1")},
|
||||
[]graph.Vertex{NewID("1"), NewID("2")},
|
||||
[]graph.Vertex{NewID("2"), NewID("3")},
|
||||
[]graph.Vertex{NewID("5"), NewID("6")},
|
||||
}
|
||||
for _, c := range expectedChannels {
|
||||
if !rt1.HasChannel(c[0], c[1], edgeIdFromString("EdgeID")) {
|
||||
t.Errorf("After addition, channel between %v and %v is absent", c[0], c[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelInfoCopy(t *testing.T) {
|
||||
c := &graph.ChannelInfo{Cpt: 10, Wgt: 10}
|
||||
c1 := c.Copy()
|
||||
if *c != *c1 {
|
||||
t.Errorf("*c.Copy() != *c, *c=%v, *c.Copy()=%v", *c, *c1)
|
||||
}
|
||||
c2 := (*graph.ChannelInfo)(nil)
|
||||
c2ans := c2.Copy()
|
||||
if c2ans != nil {
|
||||
t.Errorf("c.Copy()=%v, for c=nil, want nil", c2ans)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRTCopy(t *testing.T) {
|
||||
r := NewRoutingTable()
|
||||
// Add 2 channels
|
||||
r.AddNodes(NewID("1"), NewID("2"), NewID("3"))
|
||||
|
||||
r.AddChannel(NewID("1"), NewID("2"), edgeIdFromString("EdgeID"), &graph.ChannelInfo{10, 11})
|
||||
r.AddChannel(NewID("2"), NewID("3"), edgeIdFromString("EdgeID"), &graph.ChannelInfo{20, 21})
|
||||
// Create copy of this rt and make sure they are equal
|
||||
rCopy := r.Copy()
|
||||
channelsExp := []struct {
|
||||
id1, id2 graph.Vertex
|
||||
edgeID graph.EdgeID
|
||||
info *graph.ChannelInfo
|
||||
isErrNil bool
|
||||
}{
|
||||
{NewID("1"), NewID("2"), edgeIdFromString("EdgeID"), &graph.ChannelInfo{10, 11}, true},
|
||||
{NewID("2"), NewID("3"), edgeIdFromString("EdgeID"), &graph.ChannelInfo{20, 21}, true},
|
||||
// Channel do not exist
|
||||
{NewID("3"), NewID("4"), edgeIdFromString("EdgeID"), nil, false},
|
||||
}
|
||||
for _, c := range channelsExp {
|
||||
info, err := rCopy.GetChannelInfo(c.id1, c.id2, c.edgeID)
|
||||
if (err != nil) && (info != nil) {
|
||||
t.Errorf("rCopy.GetChannelInfo give not nil result and err")
|
||||
continue
|
||||
}
|
||||
if (err == nil) != c.isErrNil {
|
||||
t.Errorf("err == nil: %v, want: %v, err: %v", err == nil, c.isErrNil, err)
|
||||
continue
|
||||
}
|
||||
if info == nil && c.info != nil {
|
||||
t.Errorf("rCopy.GetChannelInfo(%v, %v, %v)==nil, nil. want <not nil>", c.id1, c.id2, c.edgeID)
|
||||
continue
|
||||
}
|
||||
if info != nil && c.info == nil {
|
||||
t.Errorf("rCopy.GetChannelInfo(%v, %v, %v)==<not nil>, nil. want nil", c.id1, c.id2, c.edgeID)
|
||||
continue
|
||||
}
|
||||
if (info != nil) && c.info != nil && *info != *c.info {
|
||||
t.Errorf("*rCopy.GetChannelInfo(%v, %v, %v)==%v, nil. want %v", c.id1, c.id2, c.edgeID, *info, c.info)
|
||||
}
|
||||
}
|
||||
}
|
125
routing/rt/visualizer/config.go
Normal file
125
routing/rt/visualizer/config.go
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package visualizer
|
||||
|
||||
// Configuration used for graph visualisation.
|
||||
type VisualizerConfig struct {
|
||||
// General options.
|
||||
GlobalCfg *GlobalConfig
|
||||
// Options for node visualisation.
|
||||
NodeCfg *NodeConfig
|
||||
// Options ofr highlighted nodes visualisation.
|
||||
HighlightedNodeCfg *NodeConfig
|
||||
// Options for edges visualisation.
|
||||
EdgeCfg *EdgeConfig
|
||||
// Options for highlighted edges visualisation.
|
||||
HighlightedEdgeCfg *EdgeConfig
|
||||
// Indicate whether shortcuts should be used.
|
||||
// Shortcut of a string is a prefix of a string that is unique
|
||||
// for a given set of strings.
|
||||
EnableShortcut bool
|
||||
}
|
||||
|
||||
var DefaultVisualizerConfig VisualizerConfig = VisualizerConfig{
|
||||
GlobalCfg: &DefaultGlobalConfig,
|
||||
NodeCfg: &DefaultNodeConfig,
|
||||
HighlightedNodeCfg: &DefaultHighlightedNodeConfig,
|
||||
EdgeCfg: &DefaultEdgeConfig,
|
||||
HighlightedEdgeCfg: &DefaultHighlightedEdgeConfig,
|
||||
EnableShortcut: false,
|
||||
}
|
||||
|
||||
type GlobalConfig struct {
|
||||
// Title of an image.
|
||||
Name string
|
||||
Dir bool
|
||||
// Allow multigraph.
|
||||
Strict bool
|
||||
// Background color.
|
||||
BgColor string
|
||||
}
|
||||
|
||||
var DefaultGlobalConfig GlobalConfig = GlobalConfig{
|
||||
Name: `"Routing Table"`,
|
||||
Dir: true,
|
||||
Strict: false, // Allow multigraphs
|
||||
BgColor: "black",
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
Shape string
|
||||
Style string
|
||||
FontSize string
|
||||
FontColor string
|
||||
Color string
|
||||
FillColor string
|
||||
}
|
||||
|
||||
var DefaultNodeConfig NodeConfig = NodeConfig{
|
||||
Shape: "circle",
|
||||
Style: "filled",
|
||||
FontSize: "12",
|
||||
FontColor: "black",
|
||||
Color: "white",
|
||||
FillColor: "white",
|
||||
}
|
||||
|
||||
var DefaultHighlightedNodeConfig NodeConfig = NodeConfig{
|
||||
Shape: "circle",
|
||||
Style: "filled",
|
||||
FontSize: "12",
|
||||
FontColor: "black",
|
||||
Color: "blue",
|
||||
FillColor: "blue",
|
||||
}
|
||||
|
||||
type EdgeConfig struct {
|
||||
FontSize string
|
||||
FontColor string
|
||||
Scale string
|
||||
Dir string
|
||||
Style string
|
||||
Color string
|
||||
}
|
||||
|
||||
var DefaultEdgeConfig EdgeConfig = EdgeConfig{
|
||||
FontSize: "12",
|
||||
FontColor: "gold",
|
||||
Scale: "2.5",
|
||||
Dir: "none",
|
||||
Style: "solid",
|
||||
Color: "white",
|
||||
}
|
||||
|
||||
var DefaultHighlightedEdgeConfig EdgeConfig = EdgeConfig{
|
||||
FontSize: "12",
|
||||
FontColor: "gold",
|
||||
Scale: "2.5",
|
||||
Dir: "none",
|
||||
Style: "solid",
|
||||
Color: "blue",
|
||||
}
|
||||
|
||||
func SupportedFormatsAsMap() map[string]struct{} {
|
||||
rez := make(map[string]struct{})
|
||||
for _, format := range supportedFormats {
|
||||
rez[format] = struct{}{}
|
||||
}
|
||||
return rez
|
||||
}
|
||||
|
||||
// SupportedFormats contains list of image formats that can be
|
||||
// used for output.
|
||||
func SupportedFormats() []string {
|
||||
return supportedFormats
|
||||
}
|
||||
|
||||
var supportedFormats = []string{
|
||||
"bmp", "canon", "cgimage", "cmap", "cmapx", "cmapx_np", "dot", "eps", "exr",
|
||||
"fig", "gif", "gv", "icns", "ico", "imap", "imap_np", "ismap", "jp2", "jpe",
|
||||
"jpeg", "jpg", "pct", "pdf", "pic", "pict", "plain", "plain-ext", "png",
|
||||
"pov", "ps", "ps2", "psd", "sgi", "svg", "svgz", "tga", "tif", "tiff", "tk",
|
||||
"vml", "vmlz", "xdot", "xdot1.2", "xdot1.4",
|
||||
}
|
116
routing/rt/visualizer/prefix_tree/node.go
Normal file
116
routing/rt/visualizer/prefix_tree/node.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package prefix_tree
|
||||
|
||||
import "errors"
|
||||
|
||||
// Node represents the general interface for node in prefix tree
|
||||
type Node interface {
|
||||
Name() string
|
||||
Len() int
|
||||
Child(nextSymbol string) Node
|
||||
Childs() map[string]Node
|
||||
AddChild(symbol string, child Node) error
|
||||
Parent() Node
|
||||
PreviousSymbol() string
|
||||
IsRoot() bool
|
||||
IsLeaf() bool
|
||||
IsTerminal() bool
|
||||
Terminal()
|
||||
Path() (string, error)
|
||||
}
|
||||
|
||||
type node struct {
|
||||
name string
|
||||
childs map[string]Node
|
||||
parent Node
|
||||
previousSymbol string
|
||||
isTerminal bool
|
||||
}
|
||||
|
||||
// NewNode create new node
|
||||
func NewNode(name string, parent Node, previousSymbol string, isTerminal bool) Node {
|
||||
return &node{
|
||||
name: name,
|
||||
childs: make(map[string]Node),
|
||||
parent: parent,
|
||||
previousSymbol: previousSymbol,
|
||||
isTerminal: isTerminal,
|
||||
}
|
||||
}
|
||||
|
||||
func (nd *node) Name() string {
|
||||
return nd.name
|
||||
}
|
||||
|
||||
func (nd *node) Len() int {
|
||||
return len(nd.childs)
|
||||
}
|
||||
|
||||
func (nd *node) Child(nextSymbol string) Node {
|
||||
if _, ok := nd.childs[nextSymbol]; !ok {
|
||||
return nil
|
||||
}
|
||||
return nd.childs[nextSymbol]
|
||||
}
|
||||
|
||||
func (nd *node) Childs() map[string]Node {
|
||||
return nd.childs
|
||||
}
|
||||
|
||||
func (nd *node) AddChild(symbol string, child Node) error {
|
||||
if _, ok := nd.childs[symbol]; ok {
|
||||
return errors.New("Node already exists")
|
||||
}
|
||||
nd.childs[symbol] = child
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nd *node) Parent() Node {
|
||||
return nd.parent
|
||||
}
|
||||
|
||||
func (nd *node) PreviousSymbol() string {
|
||||
return nd.previousSymbol
|
||||
}
|
||||
|
||||
func (nd *node) IsRoot() bool {
|
||||
return nd.parent == nil
|
||||
}
|
||||
|
||||
func (nd *node) IsLeaf() bool {
|
||||
return len(nd.childs) == 0
|
||||
}
|
||||
|
||||
func (nd *node) IsTerminal() bool {
|
||||
return nd.isTerminal
|
||||
}
|
||||
|
||||
func (nd *node) Terminal() {
|
||||
nd.isTerminal = true
|
||||
}
|
||||
|
||||
func (nd *node) Path() (path string, err error) {
|
||||
var xNode Node = nd
|
||||
for {
|
||||
if xNode.IsRoot() {
|
||||
break
|
||||
}
|
||||
path += xNode.PreviousSymbol()
|
||||
if xNode == xNode.Parent() {
|
||||
return "", CyclicDataError
|
||||
}
|
||||
xNode = xNode.Parent()
|
||||
}
|
||||
return Reverse(path), nil
|
||||
}
|
||||
|
||||
func Reverse(reversePath string) (path string) {
|
||||
length := len(reversePath)
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
path += string(reversePath[i])
|
||||
}
|
||||
return path
|
||||
}
|
120
routing/rt/visualizer/prefix_tree/prefix_tree.go
Normal file
120
routing/rt/visualizer/prefix_tree/prefix_tree.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package prefix_tree
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
CyclicDataError = errors.New("Cyclic data")
|
||||
NodeNotFoundError = errors.New("Node not found")
|
||||
NotEnoughData = errors.New("Not enough data")
|
||||
)
|
||||
|
||||
type PrefixTree interface {
|
||||
Add(str string) error
|
||||
Shortcut(fullname string) (string, error)
|
||||
Autocomplete(prefix string) (string, error)
|
||||
Len() int
|
||||
}
|
||||
|
||||
type prefixTree struct {
|
||||
root Node
|
||||
nodes int
|
||||
}
|
||||
|
||||
func NewPrefixTree() PrefixTree {
|
||||
root := NewNode("0", nil, "", false)
|
||||
return &prefixTree{root: root}
|
||||
}
|
||||
|
||||
func (tree *prefixTree) Add(str string) error {
|
||||
var node Node = tree.root
|
||||
for _, symbolRune := range str {
|
||||
symbol := string(byte(symbolRune))
|
||||
child := node.Child(symbol)
|
||||
if child == nil {
|
||||
child = NewNode(strconv.Itoa(tree.Len()), node, symbol, false)
|
||||
err := node.AddChild(symbol, child)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tree.nodes++
|
||||
}
|
||||
node = child
|
||||
}
|
||||
node.Terminal()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find shortcut for a given string. Shortcut is unique prefix for a given string.
|
||||
// If there is only one string in prefix tree return first symbol
|
||||
func (tree *prefixTree) Shortcut(fullname string) (string, error) {
|
||||
var node Node = tree.root
|
||||
for _, symbolRune := range fullname {
|
||||
symbol := string(byte(symbolRune))
|
||||
child := node.Child(symbol)
|
||||
if child == nil {
|
||||
return "", NodeNotFoundError
|
||||
}
|
||||
node = child
|
||||
}
|
||||
if !node.IsTerminal() {
|
||||
return "", errors.New("Node MUST be terminal")
|
||||
}
|
||||
if !node.IsLeaf() {
|
||||
return fullname, nil
|
||||
}
|
||||
highest := node
|
||||
for {
|
||||
if highest.Parent() == nil || highest.Parent().IsTerminal() || highest.Parent().Len() != 1 {
|
||||
break
|
||||
}
|
||||
if highest == highest.Parent() {
|
||||
return "", CyclicDataError
|
||||
}
|
||||
highest = highest.Parent()
|
||||
}
|
||||
path, err := highest.Path()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if path == "" {
|
||||
return fullname[0:1], nil
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (tree *prefixTree) Autocomplete(prefix string) (string, error) {
|
||||
var node Node = tree.root
|
||||
for _, symbolRune := range prefix {
|
||||
symbol := string(byte(symbolRune))
|
||||
child := node.Child(symbol)
|
||||
if child == nil {
|
||||
return "", NodeNotFoundError
|
||||
}
|
||||
node = child
|
||||
}
|
||||
for !node.IsLeaf() {
|
||||
if node.IsTerminal() || node.Len() != 1 {
|
||||
return "", NotEnoughData
|
||||
}
|
||||
childs := node.Childs()
|
||||
for _, value := range childs {
|
||||
node = value
|
||||
}
|
||||
}
|
||||
path, err := node.Path()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (tree *prefixTree) Len() int {
|
||||
return tree.nodes
|
||||
}
|
70
routing/rt/visualizer/prefix_tree/prefix_tree_test.go
Normal file
70
routing/rt/visualizer/prefix_tree/prefix_tree_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package prefix_tree
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPrefixTree(t *testing.T) {
|
||||
pt := NewPrefixTree()
|
||||
pt.Add("walk")
|
||||
pt.Add("hello")
|
||||
pt.Add("hi")
|
||||
pt.Add("hell")
|
||||
pt.Add("world")
|
||||
pt.Add("www")
|
||||
|
||||
shortcut, _ := pt.Shortcut("world")
|
||||
if expected := "wo"; shortcut != expected {
|
||||
t.Errorf("expected: %s, actual: %s", expected, shortcut)
|
||||
}
|
||||
|
||||
shortcut, _ = pt.Shortcut("www")
|
||||
if expected := "ww"; shortcut != expected {
|
||||
t.Errorf("expected: %s, actual: %s", expected, shortcut)
|
||||
}
|
||||
|
||||
autocompleted, _ := pt.Autocomplete("wo")
|
||||
if expected := "world"; autocompleted != expected {
|
||||
t.Errorf("expected: %s, actual: %s", expected, autocompleted)
|
||||
}
|
||||
|
||||
autocompleted, _ = pt.Autocomplete("ww")
|
||||
if expected := "www"; autocompleted != expected {
|
||||
t.Errorf("expected: %s, actual: %s", expected, autocompleted)
|
||||
}
|
||||
|
||||
pt.Add("123")
|
||||
pt.Add("456")
|
||||
pt.Add("1234")
|
||||
|
||||
shortcut, _ = pt.Shortcut("123")
|
||||
if expected := "123"; shortcut != expected {
|
||||
t.Errorf("expected: %s, actual: %s", expected, shortcut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefixTreeOneNode(t *testing.T) {
|
||||
pt := NewPrefixTree()
|
||||
pt.Add("123")
|
||||
shortcut, err := pt.Shortcut("123")
|
||||
if err != nil {
|
||||
t.Errorf("error getting shortcut for 123: %v, want: %v", err, nil)
|
||||
}
|
||||
expectedShortcut := "1"
|
||||
if shortcut != expectedShortcut {
|
||||
t.Errorf("expected: %v, actual: %v", expectedShortcut, shortcut)
|
||||
}
|
||||
|
||||
expectedAutocomplete := "123"
|
||||
autocomplete, err := pt.Autocomplete("123")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("error getting autocomplete for 123: %v, want: %v", err, nil)
|
||||
}
|
||||
if autocomplete != expectedAutocomplete {
|
||||
t.Errorf("expected: %v, actual: %v", expectedAutocomplete, autocomplete)
|
||||
}
|
||||
|
||||
}
|
174
routing/rt/visualizer/visualizer.go
Normal file
174
routing/rt/visualizer/visualizer.go
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright (c) 2016 Bitfury Group Limited
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
package visualizer
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/lightningnetwork/lnd/routing/rt/visualizer/prefix_tree"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/awalterschulze/gographviz"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
type visualizer struct {
|
||||
// Graph used for visualisation.
|
||||
G *graph.Graph
|
||||
// Vertexes which should be highlighted.
|
||||
HighlightedNodes []graph.Vertex
|
||||
// Edges which should be highlighted.
|
||||
HighlightedEdges []graph.Edge
|
||||
// Configuration parameters used for visualisation.
|
||||
Config *VisualizerConfig
|
||||
// Function applied to node to obtain its label
|
||||
ApplyToNode func(graph.Vertex) string
|
||||
// Function applied to edge to obtain its label
|
||||
ApplyToEdge func(*graph.ChannelInfo) string
|
||||
// Prefix used for creating shortcuts.
|
||||
pt prefix_tree.PrefixTree
|
||||
graphviz *gographviz.Graph
|
||||
}
|
||||
|
||||
// New creates new visualiser.
|
||||
func New(g *graph.Graph, highlightedNodes []graph.Vertex,
|
||||
highlightedEdges []graph.Edge, config *VisualizerConfig) *visualizer {
|
||||
if config == nil {
|
||||
config = &DefaultVisualizerConfig
|
||||
}
|
||||
return &visualizer{
|
||||
G: g,
|
||||
HighlightedNodes: highlightedNodes,
|
||||
HighlightedEdges: highlightedEdges,
|
||||
Config: config,
|
||||
ApplyToNode: func(v graph.Vertex) string { return hex.EncodeToString(v.ToByte()) },
|
||||
ApplyToEdge: func(info *graph.ChannelInfo) string { return "nil" },
|
||||
pt: prefix_tree.NewPrefixTree(),
|
||||
graphviz: gographviz.NewGraph(),
|
||||
}
|
||||
}
|
||||
|
||||
// EnableShortcut enables/disables shortcuts.
|
||||
// Shortcut is a small unique string used for labeling.
|
||||
func (viz *visualizer) EnableShortcut(value bool) {
|
||||
viz.Config.EnableShortcut = value
|
||||
}
|
||||
|
||||
// BuildPrefixTree builds prefix tree for nodes.
|
||||
// It is needed for shortcuts.
|
||||
func (viz *visualizer) BuildPrefixTree() {
|
||||
for _, node := range viz.G.GetVertexes() {
|
||||
id := viz.ApplyToNode(node)
|
||||
viz.pt.Add(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Draw creates graph representation in Graphviz dot language.
|
||||
func (viz *visualizer) Draw() string {
|
||||
viz.base()
|
||||
viz.drawNodes()
|
||||
viz.drawEdges()
|
||||
return viz.graphviz.String()
|
||||
}
|
||||
|
||||
// Base makes initialization.
|
||||
func (viz *visualizer) base() {
|
||||
viz.graphviz.SetName(viz.Config.GlobalCfg.Name)
|
||||
viz.graphviz.SetDir(viz.Config.GlobalCfg.Dir)
|
||||
viz.graphviz.SetStrict(viz.Config.GlobalCfg.Strict)
|
||||
// TODO(evg): use viz.Add(...) instead viz.Attrs.Add(...)
|
||||
viz.graphviz.Attrs.Add("bgcolor", viz.Config.GlobalCfg.BgColor)
|
||||
}
|
||||
|
||||
func (viz *visualizer) drawNodes() {
|
||||
for _, node := range viz.G.GetVertexes() {
|
||||
if viz.isHighlightedNode(node) {
|
||||
viz.drawNode(node, viz.Config.HighlightedNodeCfg)
|
||||
} else {
|
||||
viz.drawNode(node, viz.Config.NodeCfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (viz *visualizer) drawNode(node graph.Vertex, cfg *NodeConfig) {
|
||||
id := viz.ApplyToNode(node)
|
||||
if viz.Config.EnableShortcut {
|
||||
// TODO(evg): processing errors
|
||||
id, _ = viz.pt.Shortcut(id)
|
||||
}
|
||||
attrs := gographviz.Attrs{
|
||||
"shape": cfg.Shape,
|
||||
"style": cfg.Style,
|
||||
"fontsize": cfg.FontSize,
|
||||
"fontcolor": cfg.FontColor,
|
||||
"color": cfg.Color,
|
||||
"fillcolor": cfg.FillColor,
|
||||
}
|
||||
viz.graphviz.AddNode(viz.Config.GlobalCfg.Name, id, attrs)
|
||||
}
|
||||
|
||||
func (viz *visualizer) isHighlightedNode(node graph.Vertex) bool {
|
||||
for _, value := range viz.HighlightedNodes {
|
||||
if node.String() == value.String() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (viz *visualizer) drawEdges() {
|
||||
for _, edge := range viz.G.GetUndirectedEdges() {
|
||||
if viz.isHighlightedEdge(edge) {
|
||||
viz.drawEdge(edge, viz.Config.HighlightedEdgeCfg)
|
||||
} else {
|
||||
viz.drawEdge(edge, viz.Config.EdgeCfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (viz *visualizer) drawEdge(edge graph.Edge, cfg *EdgeConfig) {
|
||||
src := viz.ApplyToNode(edge.Src)
|
||||
tgt := viz.ApplyToNode(edge.Tgt)
|
||||
if viz.Config.EnableShortcut {
|
||||
// TODO(evg): processing errors
|
||||
src, _ = viz.pt.Shortcut(src)
|
||||
tgt, _ = viz.pt.Shortcut(tgt)
|
||||
}
|
||||
attrs := gographviz.Attrs{
|
||||
"fontsize": cfg.FontSize,
|
||||
"fontcolor": cfg.FontColor,
|
||||
"labeldistance": cfg.Scale,
|
||||
"dir": cfg.Dir,
|
||||
"style": cfg.Style,
|
||||
"color": cfg.Color,
|
||||
"label": viz.ApplyToEdge(edge.Info),
|
||||
}
|
||||
viz.graphviz.AddEdge(src, tgt, true, attrs)
|
||||
}
|
||||
|
||||
func (viz *visualizer) isHighlightedEdge(edge graph.Edge) bool {
|
||||
for _, value := range viz.G.GetEdges() {
|
||||
if edge == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Run graphviz command line utility (such as neato).
|
||||
// Used for creation image from textual graph representation.
|
||||
func Run(utility string, TempFile, ImageFile *os.File) error {
|
||||
extension := filepath.Ext(ImageFile.Name())[1:]
|
||||
_, err := exec.Command(utility, "-T"+extension, "-o"+ImageFile.Name(), TempFile.Name()).Output()
|
||||
return err
|
||||
}
|
||||
|
||||
// Opens file in a command line open program.
|
||||
// Used for displaying graphical files.
|
||||
func Open(ImageFile *os.File) error {
|
||||
_, err := exec.Command("open", ImageFile.Name()).Output()
|
||||
return err
|
||||
}
|
48
rpcserver.go
48
rpcserver.go
@ -12,7 +12,7 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
"github.com/btcsuite/fastsha256"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lightning-onion"
|
||||
@ -621,6 +621,16 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case nextPayment := <-payChan:
|
||||
// Query the routing table for a potential path to the
|
||||
// destination node. If a path is ultimately
|
||||
// unavailable, then an error will be returned.
|
||||
destNode := nextPayment.Dest
|
||||
targetVertex := graph.NewVertex(destNode)
|
||||
path, err := r.server.routingMgr.FindPath(targetVertex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpcsLog.Tracef("[sendpayment] selected route: %v", path)
|
||||
// If we're in debug HTLC mode, then all outgoing
|
||||
// HTLC's will pay to the same debug rHash. Otherwise,
|
||||
// we pay to the rHash specified within the RPC
|
||||
@ -631,17 +641,14 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
|
||||
} else {
|
||||
copy(rHash[:], nextPayment.PaymentHash)
|
||||
}
|
||||
|
||||
// Construct and HTLC packet which a payment route (if
|
||||
// one is found) to the destination using a Sphinx
|
||||
// onoin packet to encode the route.
|
||||
dest := hex.EncodeToString(nextPayment.Dest)
|
||||
htlcPkt, err := r.constructPaymentRoute(dest,
|
||||
htlcPkt, err := r.constructPaymentRoute([]byte(nextPayment.Dest),
|
||||
nextPayment.Amt, rHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We launch a new goroutine to execute the current
|
||||
// payment so we can continue to serve requests while
|
||||
// this payment is being dispatiched.
|
||||
@ -695,7 +702,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
|
||||
// Construct and HTLC packet which a payment route (if
|
||||
// one is found) to the destination using a Sphinx
|
||||
// onoin packet to encode the route.
|
||||
htlcPkt, err := r.constructPaymentRoute(nextPayment.DestString,
|
||||
htlcPkt, err := r.constructPaymentRoute([]byte(nextPayment.DestString),
|
||||
nextPayment.Amt, rHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -714,7 +721,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
|
||||
// encapsulates a Sphinx onion packet that encodes the end-to-end route any
|
||||
// payment instructions necessary to complete an HTLC. If a route is unable to
|
||||
// be located, then an error is returned indicating as much.
|
||||
func (r *rpcServer) constructPaymentRoute(destPubkey string, amt int64,
|
||||
func (r *rpcServer) constructPaymentRoute(destPubkey []byte, amt int64,
|
||||
rHash [32]byte) (*htlcPacket, error) {
|
||||
|
||||
const queryTimeout = time.Duration(time.Second * 10)
|
||||
@ -722,9 +729,8 @@ func (r *rpcServer) constructPaymentRoute(destPubkey string, amt int64,
|
||||
// Query the routing table for a potential path to the destination
|
||||
// node. If a path is ultimately unavailable, then an error will be
|
||||
// returned.
|
||||
targetVertex := graph.NewID(destPubkey)
|
||||
path, err := r.server.routingMgr.FindPath(targetVertex,
|
||||
queryTimeout)
|
||||
targetVertex := graph.NewVertex(destPubkey)
|
||||
path, err := r.server.routingMgr.FindPath(targetVertex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -747,10 +753,7 @@ func (r *rpcServer) constructPaymentRoute(destPubkey string, amt int64,
|
||||
OnionBlob: sphinxPacket,
|
||||
}
|
||||
|
||||
firstHopPub, err := hex.DecodeString(path[1].String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
firstHopPub := path[1].ToByte()
|
||||
destInterface := wire.ShaHash(fastsha256.Sum256(firstHopPub))
|
||||
|
||||
return &htlcPacket{
|
||||
@ -763,17 +766,14 @@ func (r *rpcServer) constructPaymentRoute(destPubkey string, amt int64,
|
||||
// the onion route specified by the passed list of graph vertexes. The blob
|
||||
// returned from this function can immediately be included within an HTLC add
|
||||
// packet to be sent to the first hop within the route.
|
||||
func generateSphinxPacket(vertexes []graph.ID, paymentHash []byte) ([]byte, error) {
|
||||
func generateSphinxPacket(vertexes []graph.Vertex, paymentHash []byte) ([]byte, error) {
|
||||
// First convert all the vertexs from the routing table to in-memory
|
||||
// public key objects. These objects are necessary in order to perform
|
||||
// the series of ECDH operations required to construct the Sphinx
|
||||
// packet below.
|
||||
route := make([]*btcec.PublicKey, len(vertexes))
|
||||
for i, vertex := range vertexes {
|
||||
vertexBytes, err := hex.DecodeString(vertex.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vertexBytes := vertex.ToByte()
|
||||
|
||||
pub, err := btcec.ParsePubKey(vertexBytes, btcec.S256())
|
||||
if err != nil {
|
||||
@ -1091,11 +1091,11 @@ func (r *rpcServer) ShowRoutingTable(ctx context.Context,
|
||||
for _, channel := range rtCopy.AllChannels() {
|
||||
channels = append(channels,
|
||||
&lnrpc.RoutingTableLink{
|
||||
Id1: channel.Id1.String(),
|
||||
Id2: channel.Id2.String(),
|
||||
Outpoint: channel.EdgeID.String(),
|
||||
Capacity: channel.Info.Capacity(),
|
||||
Weight: channel.Info.Weight(),
|
||||
Id1: hex.EncodeToString(channel.Src.ToByte()),
|
||||
Id2: hex.EncodeToString(channel.Tgt.ToByte()),
|
||||
Outpoint: channel.Id.String(),
|
||||
Capacity: channel.Info.Cpt,
|
||||
Weight: channel.Info.Wgt,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
63
server.go
63
server.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
@ -18,8 +17,8 @@ import (
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcutil"
|
||||
|
||||
"github.com/BitfuryLightning/tools/routing"
|
||||
"github.com/BitfuryLightning/tools/rt/graph"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/rt/graph"
|
||||
)
|
||||
|
||||
// server is the main server of the Lightning Network Daemon. The server
|
||||
@ -122,8 +121,36 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
|
||||
|
||||
// Create a new routing manager with ourself as the sole node within
|
||||
// the graph.
|
||||
selfVertex := hex.EncodeToString(serializedPubKey)
|
||||
s.routingMgr = routing.NewRoutingManager(graph.NewID(selfVertex), nil)
|
||||
selfVertex := serializedPubKey
|
||||
routingMgrConfig := &routing.RoutingConfig{}
|
||||
routingMgrConfig.SendMessage = func (receiver [33]byte, msg lnwire.Message) error {
|
||||
receiverID := graph.NewVertex(receiver[:])
|
||||
if receiverID == graph.NilVertex {
|
||||
peerLog.Critical("receiverID == graph.NilVertex")
|
||||
return fmt.Errorf("receiverID == graph.NilVertex")
|
||||
}
|
||||
|
||||
var targetPeer *peer
|
||||
for _, peer := range s.peers { // TODO: threadsafe api
|
||||
nodePub := peer.addr.IdentityKey.SerializeCompressed()
|
||||
nodeVertex := graph.NewVertex(nodePub[:])
|
||||
|
||||
// We found the the target
|
||||
if receiverID == nodeVertex {
|
||||
targetPeer = peer
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if targetPeer != nil {
|
||||
targetPeer.queueMsg(msg, nil)
|
||||
} else {
|
||||
srvrLog.Errorf("Can't find peer to send message %v",
|
||||
receiverID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
s.routingMgr = routing.NewRoutingManager(graph.NewVertex(selfVertex), routingMgrConfig)
|
||||
s.htlcSwitch = newHtlcSwitch(serializedPubKey, s.routingMgr)
|
||||
|
||||
s.rpcServer = newRpcServer(s)
|
||||
@ -308,32 +335,6 @@ out:
|
||||
case *openChanReq:
|
||||
s.handleOpenChanReq(msg)
|
||||
}
|
||||
case msg := <-s.routingMgr.ChOut:
|
||||
msg1 := msg.(*routing.RoutingMessage)
|
||||
if msg1.ReceiverID == nil {
|
||||
peerLog.Critical("msg1.GetReceiverID() == nil")
|
||||
continue
|
||||
}
|
||||
receiverID := msg1.ReceiverID.String()
|
||||
|
||||
var targetPeer *peer
|
||||
for _, peer := range s.peers { // TODO: threadsafe api
|
||||
nodePub := peer.addr.IdentityKey.SerializeCompressed()
|
||||
idStr := hex.EncodeToString(nodePub)
|
||||
|
||||
// We found the the target
|
||||
if receiverID == idStr {
|
||||
targetPeer = peer
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if targetPeer != nil {
|
||||
targetPeer.queueMsg(msg1.Msg, nil)
|
||||
} else {
|
||||
srvrLog.Errorf("Can't find peer to send message %v",
|
||||
receiverID)
|
||||
}
|
||||
case <-s.quit:
|
||||
break out
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user