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:
BitfuryLightning 2016-10-05 16:47:02 -04:00 committed by Olaoluwa Osuntokun
parent da3028e10c
commit 327768f4ad
37 changed files with 3152 additions and 308 deletions

View File

@ -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())
}
return "nil"
viz.ApplyToNode = func(v graph.Vertex) string {
return hex.EncodeToString(v.ToByte())
}
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,
},
)
}

View File

@ -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
View File

@ -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

View File

@ -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

View File

@ -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 "+

View File

@ -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)
}

View File

@ -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:

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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 {

View 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")
}
}

View File

@ -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)

View 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)
}
}

View File

@ -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)

View File

@ -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
View File

@ -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
View 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
View 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
View 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
View 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
}

View 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)
}
}
}

View 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
}

View 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
View 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)
}

View 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
View 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
}

View 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
View 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
View 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
View 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)
}
}
}

View 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",
}

View 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
}

View 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
}

View 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)
}
}

View 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
}

View File

@ -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,
},
)
}

View File

@ -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
}