mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-20 10:39:19 +01:00
added dot generation in router
This commit is contained in:
parent
d182d9db6e
commit
6d28879ebb
@ -1,17 +1,20 @@
|
||||
package fr.acinq.eclair.router
|
||||
|
||||
import java.io.{ByteArrayOutputStream, OutputStreamWriter}
|
||||
|
||||
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
|
||||
import akka.pattern.pipe
|
||||
import fr.acinq.bitcoin.Crypto.PublicKey
|
||||
import fr.acinq.bitcoin.Script.{pay2wsh, write}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.bitcoin.{BinaryData, Transaction}
|
||||
import fr.acinq.eclair._
|
||||
import fr.acinq.eclair.blockchain.{GetTx, GetTxResponse, WatchEventSpent, WatchSpent}
|
||||
import fr.acinq.eclair.channel._
|
||||
import fr.acinq.eclair.transactions.Scripts
|
||||
import fr.acinq.eclair.wire._
|
||||
import org.jgrapht.alg.shortestpath.DijkstraShortestPath
|
||||
import org.jgrapht.graph.{DefaultDirectedGraph, DefaultEdge}
|
||||
import org.jgrapht.ext._
|
||||
import org.jgrapht.graph.{DefaultDirectedGraph, DefaultEdge, SimpleGraph}
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.concurrent.duration._
|
||||
@ -42,11 +45,11 @@ class Router(watcher: ActorRef) extends Actor with ActorLogging {
|
||||
def receive: Receive = main(nodes = Map(), channels = Map(), updates = Map(), rebroadcast = Nil, awaiting = Set(), stash = Nil)
|
||||
|
||||
def mainWithLog(nodes: Map[BinaryData, NodeAnnouncement],
|
||||
channels: Map[Long, ChannelAnnouncement],
|
||||
updates: Map[ChannelDesc, ChannelUpdate],
|
||||
rebroadcast: Seq[RoutingMessage],
|
||||
awaiting: Set[ChannelAnnouncement],
|
||||
stash: Seq[RoutingMessage]) = {
|
||||
channels: Map[Long, ChannelAnnouncement],
|
||||
updates: Map[ChannelDesc, ChannelUpdate],
|
||||
rebroadcast: Seq[RoutingMessage],
|
||||
awaiting: Set[ChannelAnnouncement],
|
||||
stash: Seq[RoutingMessage]) = {
|
||||
log.info(s"current status channels=${channels.size} nodes=${nodes.size} updates=${updates.size}")
|
||||
main(nodes, channels, updates, rebroadcast, awaiting, stash)
|
||||
}
|
||||
@ -167,6 +170,8 @@ class Router(watcher: ActorRef) extends Actor with ActorLogging {
|
||||
|
||||
case 'updates => sender ! updates.values
|
||||
|
||||
case 'dot => graph2dot(nodes, channels) pipeTo sender
|
||||
|
||||
case RouteRequest(start, end) => findRoute(start, end, updates).map(RouteResponse(_)) pipeTo sender
|
||||
|
||||
case other => log.warning(s"unhandled message $other")
|
||||
@ -203,4 +208,40 @@ object Router {
|
||||
findRouteDijkstra(localNodeId, targetNodeId, updates.keys)
|
||||
.map(desc => Hop(desc.a, desc.b, updates(desc)))
|
||||
}
|
||||
|
||||
def graph2dot(nodes: Map[BinaryData, NodeAnnouncement], channels: Map[Long, ChannelAnnouncement])(implicit ec: ExecutionContext): Future[BinaryData] = Future {
|
||||
case class DescEdge(channelId: Long) extends DefaultEdge
|
||||
val g = new SimpleGraph[BinaryData, DescEdge](classOf[DescEdge])
|
||||
channels.foreach(d => {
|
||||
g.addVertex(d._2.nodeId1)
|
||||
g.addVertex(d._2.nodeId2)
|
||||
g.addEdge(d._2.nodeId1, d._2.nodeId2, new DescEdge(d._1))
|
||||
})
|
||||
val vertexIDProvider = new ComponentNameProvider[BinaryData]() {
|
||||
override def getName(nodeId: BinaryData): String = "\"" + nodeId.toString() + "\""
|
||||
}
|
||||
val edgeLabelProvider = new ComponentNameProvider[DescEdge]() {
|
||||
override def getName(e: DescEdge): String = e.channelId.toString
|
||||
}
|
||||
val vertexAttributeProvider = new ComponentAttributeProvider[BinaryData]() {
|
||||
|
||||
override def getComponentAttributes(nodeId: BinaryData): java.util.Map[String, String] =
|
||||
|
||||
nodes.get(nodeId) match {
|
||||
case Some(ann) => Map("label" -> ann.alias, "color" -> f"#${ann.rgbColor._1}%02x${ann.rgbColor._2}%02x${ann.rgbColor._3}%02x")
|
||||
case None => Map.empty[String, String]
|
||||
}
|
||||
}
|
||||
val exporter = new DOTExporter[BinaryData, DescEdge](vertexIDProvider, null, edgeLabelProvider, vertexAttributeProvider, null)
|
||||
val bos = new ByteArrayOutputStream()
|
||||
val writer = new OutputStreamWriter(bos)
|
||||
try {
|
||||
exporter.exportGraph(g, writer)
|
||||
bos.toByteArray
|
||||
} finally {
|
||||
writer.close()
|
||||
bos.close()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,12 +31,12 @@ abstract class BaseRouterSpec extends TestkitBaseClass {
|
||||
|
||||
val DUMMY_SIG = BinaryData("3045022100e0a180fdd0fe38037cc878c03832861b40a29d32bd7b40b10c9e1efc8c1468a002205ae06d1624896d0d29f4b31e32772ea3cb1b4d7ed4e077e5da28dcc33c0e781201")
|
||||
|
||||
val ann_a = NodeAnnouncement(DUMMY_SIG, 0, a, (0, 0, 0), "node-A", "0000", Nil)
|
||||
val ann_b = NodeAnnouncement(DUMMY_SIG, 0, b, (0, 0, 0), "node-B", "0000", Nil)
|
||||
val ann_c = NodeAnnouncement(DUMMY_SIG, 0, c, (0, 0, 0), "node-C", "0000", Nil)
|
||||
val ann_d = NodeAnnouncement(DUMMY_SIG, 0, d, (0, 0, 0), "node-D", "0000", Nil)
|
||||
val ann_e = NodeAnnouncement(DUMMY_SIG, 0, e, (0, 0, 0), "node-E", "0000", Nil)
|
||||
val ann_f = NodeAnnouncement(DUMMY_SIG, 0, f, (0, 0, 0), "node-F", "0000", Nil)
|
||||
val ann_a = NodeAnnouncement(DUMMY_SIG, 0, a, (15, 10, -70), "node-A", "0000", Nil)
|
||||
val ann_b = NodeAnnouncement(DUMMY_SIG, 0, b, (50, 99, -80), "node-B", "0000", Nil)
|
||||
val ann_c = NodeAnnouncement(DUMMY_SIG, 0, c, (123, 100, -40), "node-C", "0000", Nil)
|
||||
val ann_d = NodeAnnouncement(DUMMY_SIG, 0, d, (-120, -20, 60), "node-D", "0000", Nil)
|
||||
val ann_e = NodeAnnouncement(DUMMY_SIG, 0, e, (-50, 0, 10), "node-E", "0000", Nil)
|
||||
val ann_f = NodeAnnouncement(DUMMY_SIG, 0, f, (30, 10, -50), "node-F", "0000", Nil)
|
||||
|
||||
val channelId_ab = toShortId(420000, 1, 0)
|
||||
val channelId_bc = toShortId(420000, 2, 0)
|
||||
|
@ -1,9 +1,13 @@
|
||||
package fr.acinq.eclair.router
|
||||
|
||||
import java.io.{ByteArrayInputStream, ByteArrayOutputStream, File}
|
||||
import javafx.application.Platform
|
||||
|
||||
import akka.actor.Status.Failure
|
||||
import akka.testkit.TestProbe
|
||||
import com.google.common.io.Files
|
||||
import fr.acinq.bitcoin.Script.{pay2wsh, write}
|
||||
import fr.acinq.bitcoin.{Satoshi, Transaction, TxOut}
|
||||
import fr.acinq.bitcoin.{BinaryData, Satoshi, Transaction, TxOut}
|
||||
import fr.acinq.eclair.blockchain.{GetTx, GetTxResponse, WatchEventSpent, WatchSpent}
|
||||
import fr.acinq.eclair.channel.BITCOIN_FUNDING_OTHER_CHANNEL_SPENT
|
||||
import fr.acinq.eclair.toShortId
|
||||
@ -59,7 +63,7 @@ class RouterSpec extends BaseRouterSpec {
|
||||
|
||||
}
|
||||
|
||||
test("route not found (unreachable target)") { case (router, watcher) =>
|
||||
test("route not found (unreachable target)") { case (router, _) =>
|
||||
val sender = TestProbe()
|
||||
// no route a->f
|
||||
sender.send(router, RouteRequest(a, f))
|
||||
@ -67,7 +71,7 @@ class RouterSpec extends BaseRouterSpec {
|
||||
assert(res.cause.getMessage === "route not found")
|
||||
}
|
||||
|
||||
test("route not found (non-existing source)") { case (router, watcher) =>
|
||||
test("route not found (non-existing source)") { case (router, _) =>
|
||||
val sender = TestProbe()
|
||||
// no route a->f
|
||||
sender.send(router, RouteRequest(randomPubkey, f))
|
||||
@ -75,7 +79,7 @@ class RouterSpec extends BaseRouterSpec {
|
||||
assert(res.cause.getMessage === "graph must contain the source vertex")
|
||||
}
|
||||
|
||||
test("route not found (non-existing target)") { case (router, watcher) =>
|
||||
test("route not found (non-existing target)") { case (router, _) =>
|
||||
val sender = TestProbe()
|
||||
// no route a->f
|
||||
sender.send(router, RouteRequest(a, randomPubkey))
|
||||
@ -83,7 +87,7 @@ class RouterSpec extends BaseRouterSpec {
|
||||
assert(res.cause.getMessage === "graph must contain the sink vertex")
|
||||
}
|
||||
|
||||
test("route found") { case (router, watcher) =>
|
||||
test("route found") { case (router, _) =>
|
||||
val sender = TestProbe()
|
||||
sender.send(router, RouteRequest(a, d))
|
||||
val res = sender.expectMsgType[RouteResponse]
|
||||
@ -91,4 +95,18 @@ class RouterSpec extends BaseRouterSpec {
|
||||
assert(res.hops.last.nextNodeId === d.toBin)
|
||||
}
|
||||
|
||||
ignore("export graph in dot format") { case (router, _) =>
|
||||
val sender = TestProbe()
|
||||
sender.send(router, 'dot)
|
||||
val dot = sender.expectMsgType[BinaryData]
|
||||
Files.write(dot.toArray, new File("graph.dot"))
|
||||
|
||||
import scala.sys.process._
|
||||
val input = new ByteArrayInputStream(dot.toArray)
|
||||
val output = new ByteArrayOutputStream()
|
||||
"dot -Tpng" #< input #> output !
|
||||
val img = output.toByteArray
|
||||
Files.write(img, new File("graph.png"))
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user