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 authored and Olaoluwa Osuntokun committed 2016-11-23 20:37:43 -06:00
1 parent da3028e10c
commit 327768f4ad
37 files changed
+3152 -308

No files matched your search

+470
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
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
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
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
}
+31
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)
}
}
}
+67
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
}
+41
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
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)
}
+166
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
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
}
+39
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
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
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
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)
}
}
}
+125
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",
}
+116
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
}
@@ -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
}
@@ -0,0 +1,70 @@
// Copyright (c) 2016 Bitfury Group Limited
// Distributed under the MIT software license, see the accompanying
// file LICENSE or http://www.opensource.org/licenses/mit-license.php
package prefix_tree
import "testing"
func TestPrefixTree(t *testing.T) {
pt := NewPrefixTree()
pt.Add("walk")
pt.Add("hello")
pt.Add("hi")
pt.Add("hell")
pt.Add("world")
pt.Add("www")
shortcut, _ := pt.Shortcut("world")
if expected := "wo"; shortcut != expected {
t.Errorf("expected: %s, actual: %s", expected, shortcut)
}
shortcut, _ = pt.Shortcut("www")
if expected := "ww"; shortcut != expected {
t.Errorf("expected: %s, actual: %s", expected, shortcut)
}
autocompleted, _ := pt.Autocomplete("wo")
if expected := "world"; autocompleted != expected {
t.Errorf("expected: %s, actual: %s", expected, autocompleted)
}
autocompleted, _ = pt.Autocomplete("ww")
if expected := "www"; autocompleted != expected {
t.Errorf("expected: %s, actual: %s", expected, autocompleted)
}
pt.Add("123")
pt.Add("456")
pt.Add("1234")
shortcut, _ = pt.Shortcut("123")
if expected := "123"; shortcut != expected {
t.Errorf("expected: %s, actual: %s", expected, shortcut)
}
}
func TestPrefixTreeOneNode(t *testing.T) {
pt := NewPrefixTree()
pt.Add("123")
shortcut, err := pt.Shortcut("123")
if err != nil {
t.Errorf("error getting shortcut for 123: %v, want: %v", err, nil)
}
expectedShortcut := "1"
if shortcut != expectedShortcut {
t.Errorf("expected: %v, actual: %v", expectedShortcut, shortcut)
}
expectedAutocomplete := "123"
autocomplete, err := pt.Autocomplete("123")
if err != nil {
t.Errorf("error getting autocomplete for 123: %v, want: %v", err, nil)
}
if autocomplete != expectedAutocomplete {
t.Errorf("expected: %v, actual: %v", expectedAutocomplete, autocomplete)
}
}
+174
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
}