diff --git a/cmd/opera/launcher/export.go b/cmd/opera/launcher/export.go
index 4277a2e2f..5751b7c08 100644
--- a/cmd/opera/launcher/export.go
+++ b/cmd/opera/launcher/export.go
@@ -18,7 +18,6 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/status-im/keycard-go/hexutils"
"github.com/syndtr/goleveldb/leveldb/opt"
- "gonum.org/v1/gonum/graph/encoding/dot"
"gopkg.in/urfave/cli.v1"
"github.com/Fantom-foundation/go-opera/gossip"
@@ -143,12 +142,8 @@ func exportDOT(writer io.Writer, gdb *gossip.Store, cfg *config, from, to idx.Ep
}
graph := dag.Graph(gdb, consensusCfg, from, to)
- buf, err := dot.Marshal(graph, "DAG", "", "\t")
- if err != nil {
- return err
- }
- _, err = writer.Write(buf)
+ _, err = writer.Write([]byte(graph.String()))
if err != nil {
return err
}
diff --git a/go.mod b/go.mod
index c3dbc3e71..c21bea709 100644
--- a/go.mod
+++ b/go.mod
@@ -43,8 +43,6 @@ require (
gopkg.in/urfave/cli.v1 v1.20.0
)
-require gonum.org/v1/gonum v0.13.0
-
require (
github.com/DataDog/zstd v1.4.5 // indirect
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
diff --git a/go.sum b/go.sum
index d3779cf8b..d630269a6 100644
--- a/go.sum
+++ b/go.sum
@@ -35,8 +35,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
-git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
@@ -74,10 +72,7 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
-github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
-github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
-github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -105,8 +100,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
@@ -209,7 +202,6 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlK
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -231,13 +223,6 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclK
github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
-github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
-github.com/go-fonts/latin-modern v0.3.0/go.mod h1:ysEQXnuT/sCDOAONxC7ImeEDVINbltClhasMAqEtRK0=
-github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
-github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
-github.com/go-fonts/liberation v0.3.0/go.mod h1:jdJ+cqF+F4SUL2V+qxBth8fvBpBDS7yloUL5Fi8GTGY=
-github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -245,9 +230,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
-github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
-github.com/go-latex/latex v0.0.0-20230307184459-12ec69307ad9/go.mod h1:gWuR/CrFDDeVRFQwHPvsv9soJVB/iqymhuZQuJ3a9OM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
@@ -257,8 +239,6 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
-github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@@ -421,7 +401,6 @@ github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0t
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
@@ -538,9 +517,6 @@ github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHu
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
-github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
-github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
-github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
@@ -592,8 +568,6 @@ github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
-github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
@@ -717,7 +691,6 @@ golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -730,17 +703,6 @@ golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZ
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
-golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
-golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -884,7 +846,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -948,7 +909,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -995,14 +955,9 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
-gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
-gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM=
-gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU=
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
-gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
-gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
diff --git a/utils/dag/dag.go b/utils/dag/dag.go
index be3eca67c..0630322f2 100644
--- a/utils/dag/dag.go
+++ b/utils/dag/dag.go
@@ -2,19 +2,13 @@ package dag
import (
"github.com/Fantom-foundation/lachesis-base/inter/idx"
- "gonum.org/v1/gonum/graph/encoding/dot"
"github.com/Fantom-foundation/go-opera/gossip"
"github.com/Fantom-foundation/go-opera/integration"
+ "github.com/Fantom-foundation/go-opera/utils/dag/dot"
)
-func Graph(db *gossip.Store, cfg integration.Configs, from, to idx.Epoch) dot.Graph {
- /* g:= &graphOnDisk{
- db: db,
- epochFrom: from,
- epochTo: to,
- }*/
-
+func Graph(db *gossip.Store, cfg integration.Configs, from, to idx.Epoch) *dot.Graph {
g := readDagGraph(db, cfg, from, to)
return g
diff --git a/utils/dag/dot.go b/utils/dag/dot.go
deleted file mode 100644
index 1b0978909..000000000
--- a/utils/dag/dot.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package dag
-
-import (
- "fmt"
-
- "github.com/Fantom-foundation/lachesis-base/inter/idx"
-
- "github.com/Fantom-foundation/lachesis-base/hash"
- "github.com/Fantom-foundation/lachesis-base/inter/dag"
- "gonum.org/v1/gonum/graph"
- "gonum.org/v1/gonum/graph/encoding"
-)
-
-// dotEdge is a graph edge. In directed graphs, the direction of the
-// edge is given from -> to, otherwise the edge is semantically
-// unordered.
-type dotEdge struct {
- x, y *dotNode
- attributer
-}
-
-// From returns the from node of the edge.
-func (e *dotEdge) From() graph.Node {
- return e.x
-}
-
-// To returns the to node of the edge.
-func (e *dotEdge) To() graph.Node {
- return e.y
-}
-
-// ReversedEdge returns the edge reversal of the receiver
-// if a reversal is valid for the data type.
-// When a reversal is valid an edge of the same type as
-// the receiver with nodes of the receiver swapped should
-// be returned, otherwise the receiver should be returned
-// unaltered.
-func (e *dotEdge) ReversedEdge() graph.Edge {
- return nil
-}
-
-// dotNode is a graph node.
-type dotNode struct {
- id int64
- creator idx.ValidatorID
- hash hash.Event
- parents hash.Events
- attributer
-}
-
-func newDotNode(id int64, e dag.Event) *dotNode {
- n := &dotNode{
- id: id,
- creator: e.Creator(),
- hash: e.ID(),
- parents: e.Parents(),
- attributer: attributer(make(map[string]string, 10)),
- }
- n.setAttr("label", fmt.Sprintf("%s\n%d", n.hash.String(), e.Creator()))
- return n
-}
-
-func (n *dotNode) ID() int64 {
- return n.id
-}
-
-type dagNodes struct {
- data chan *dotNode
- current *dotNode
-}
-
-// Reset returns the iterator to its start position.
-func (nn *dagNodes) Reset() {
- panic("Not implemented yet")
-}
-
-// Next advances the iterator and returns whether
-// the next call to the item method will return a
-// non-nil item.
-//
-// Next should be called prior to any call to the
-// iterator's item retrieval method after the
-// iterator has been obtained or reset.
-//
-// The order of iteration is implementation
-// dependent.
-func (nn *dagNodes) Next() bool {
- nn.current = <-nn.data
- return nn.current != nil
-}
-
-// Node returns the current Node from the iterator.
-func (nn *dagNodes) Node() graph.Node {
- return nn.current
-}
-
-// Len returns the number of items remaining in the
-// iterator.
-//
-// If the number of items in the iterator is unknown,
-// too large to materialize or too costly to calculate
-// then Len may return a negative value.
-// In this case the consuming function must be able
-// to operate on the items of the iterator directly
-// without materializing the items into a slice.
-// The magnitude of a negative length has
-// implementation-dependent semantics.
-func (nn *dagNodes) Len() int {
- return -1
-}
-
-// --
-
-// Attributer implements encoding.Attributer over kv-map
-type attributer map[string]string
-
-// defines graph.Node or graph.Edge values that can
-// specify graph attributes.
-func (a attributer) Attributes() []encoding.Attribute {
- aa := make([]encoding.Attribute, 0, len(a))
-
- for k, v := range a {
- aa = append(aa,
- encoding.Attribute{
- Key: k,
- Value: v,
- })
- }
-
- return aa
-}
-
-func (a attributer) setAttr(key, val string) {
- if val == "" {
- delete(a, key)
- return
- }
- a[key] = val
-}
diff --git a/utils/dag/dot/LICENSE.md b/utils/dag/dot/LICENSE.md
new file mode 100644
index 000000000..99daa6bda
--- /dev/null
+++ b/utils/dag/dot/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright (C) 2012 Travis Cline
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/utils/dag/dot/README.md b/utils/dag/dot/README.md
new file mode 100644
index 000000000..bc40901cc
--- /dev/null
+++ b/utils/dag/dot/README.md
@@ -0,0 +1,16 @@
+dot
+===
+
+dot language support for Go
+
+
+See http://godoc.org/github.com/tmc/dot
+
+Todo:
+
+* parser
+* regression suite
+
+The pydot library used as a reference
+
+License: MIT
diff --git a/utils/dag/dot/dot.go b/utils/dag/dot/dot.go
new file mode 100644
index 000000000..0c3ec2429
--- /dev/null
+++ b/utils/dag/dot/dot.go
@@ -0,0 +1,531 @@
+/*
+Package dot implements an API to produce Graphviz dot language output.
+
+Basic Graph creation:
+
+ g := dot.NewGraph("G")
+ g.SetType(dot.DIGRAPH)
+ ...
+ g.AddEdge(dot.NewNode("A"), dot.NewNode("B"))
+ ...
+ fmt.Sprint(g)
+*/
+package dot
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+var AttributeError = errors.New("Invalid Attribute")
+
+var graphAttributes = []string{"Damping", "K", "URL", "aspect", "bb", "bgcolor",
+ "center", "charset", "clusterrank", "colorscheme", "comment", "compound",
+ "concentrate", "defaultdist", "dim", "dimen", "diredgeconstraints",
+ "dpi", "epsilon", "esep", "fontcolor", "fontname", "fontnames",
+ "fontpath", "fontsize", "id", "label", "labeljust", "labelloc",
+ "landscape", "layers", "layersep", "layout", "levels", "levelsgap",
+ "lheight", "lp", "lwidth", "margin", "maxiter", "mclimit", "mindist",
+ "mode", "model", "mosek", "nodesep", "nojustify", "normalize", "nslimit",
+ "nslimit1", "ordering", "orientation", "outputorder", "overlap",
+ "overlap_scaling", "pack", "packmode", "pad", "page", "pagedir",
+ "quadtree", "quantum", "rankdir", "ranksep", "ratio", "remincross",
+ "repulsiveforce", "resolution", "root", "rotate", "searchsize", "sep",
+ "showboxes", "style", "size", "smoothing", "sortv", "splines", "start",
+ "stylesheet", "target", "truecolor", "viewport", "voro_margin",
+ "rank", "newrank"}
+
+var edgeAttributes = []string{"URL", "arrowhead", "arrowsize", "arrowtail",
+ "color", "colorscheme", "comment", "constraint", "decorate", "dir",
+ "edgeURL", "edgehref", "edgetarget", "edgetooltip", "fontcolor",
+ "fontname", "fontsize", "headURL", "headclip", "headhref", "headlabel",
+ "headport", "headtarget", "headtooltip", "href", "id", "label",
+ "labelURL", "labelangle", "labeldistance", "labelfloat", "labelfontcolor",
+ "labelfontname", "labelfontsize", "labelhref", "labeltarget",
+ "labeltooltip", "layer", "len", "lhead", "lp", "ltail", "minlen",
+ "nojustify", "penwidth", "pos", "samehead", "sametail", "showboxes",
+ "style", "tailURL", "tailclip", "tailhref", "taillabel", "tailport",
+ "tailtarget", "tailtooltip", "target", "tooltip", "weight",
+ // for subgraphs
+ "rank"}
+
+var nodeAttributes = []string{"URL", "color", "colorscheme", "comment",
+ "distortion", "fillcolor", "fixedsize", "fontcolor", "fontname",
+ "fontsize", "group", "height", "id", "image", "imagescale", "label",
+ "labelloc", "layer", "margin", "nojustify", "orientation", "penwidth",
+ "peripheries", "pin", "pos", "rects", "regular", "root", "samplepoints",
+ "shape", "shapefile", "showboxes", "sides", "skew", "sortv", "style",
+ "target", "tooltip", "vertices", "width", "z",
+ // The following are attributes dot2tex
+ "texlbl", "texmode"}
+
+var clusterAttributes = []string{"K", "URL", "bgcolor", "color", "colorscheme",
+ "fillcolor", "fontcolor", "fontname", "fontsize", "label", "labeljust",
+ "labelloc", "lheight", "lp", "lwidth", "nojustify", "pencolor",
+ "penwidth", "peripheries", "sortv", "style", "target", "tooltip"}
+
+var dotKeywords = []string{"graph", "subgraph", "digraph", "node", "edge", "strict"}
+
+type GraphType int
+
+const (
+ DIGRAPH GraphType = iota
+ GRAPH
+ SUBGRAPH
+)
+
+// Fields common to all graph object types
+type common struct {
+ _type string
+ name string
+ attributes map[string]string
+ sequence int
+ parentGraph *Graph
+}
+
+type GraphObject interface {
+ Type() string
+ Get(string) string
+ Set(string, string) error
+ GetParentGraph() *Graph
+ SetParentGraph(g *Graph)
+ Sequence() int
+}
+
+type graphObjects []GraphObject
+
+func (gol graphObjects) Len() int {
+ return len(gol)
+}
+
+func (gol graphObjects) Less(i, j int) bool {
+ return gol[i].Sequence() < gol[j].Sequence()
+}
+
+func (gol graphObjects) Swap(i, j int) {
+ gol[i], gol[j] = gol[j], gol[i]
+}
+
+type Graph struct {
+ common
+ nodeAttributes map[string]string
+ edgeAttributes map[string]string
+ sameRank [][]string
+ strict bool
+ graphType GraphType
+ supressDisconnected bool
+ simplify bool
+ currentChildSequence int
+ nodes map[string][]*Node
+ edges map[string][]*Edge
+ subgraphs map[string][]*SubGraph
+}
+
+func NewGraph(name string) *Graph {
+ g := &Graph{
+ common: common{
+ _type: "graph",
+ name: name,
+ attributes: make(map[string]string, 0),
+ },
+ nodeAttributes: make(map[string]string),
+ edgeAttributes: make(map[string]string),
+ sameRank: make([][]string, 0),
+ nodes: make(map[string][]*Node, 0),
+ edges: make(map[string][]*Edge, 0),
+ subgraphs: make(map[string][]*SubGraph, 0),
+ currentChildSequence: 1,
+ }
+ g.SetParentGraph(g)
+ return g
+}
+
+type SubGraph struct {
+ Graph
+}
+
+func NewSubgraph(name string) *SubGraph {
+ result := &SubGraph{
+ Graph: *NewGraph(name),
+ }
+ result._type = "subgraph"
+ result.graphType = SUBGRAPH
+ return result
+}
+
+func indexInSlice(slice []string, toFind string) int {
+ for i, v := range slice {
+ if v == toFind {
+ return i
+ }
+ }
+ return -1
+}
+
+var alreadyQuotedRegex = regexp.MustCompile("^\".+\"$")
+var validIdentifierRegexWithPort = regexp.MustCompile("^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$")
+var validIdentifierRegex = regexp.MustCompile("^[_a-zA-Z][a-zA-Z0-9_,]*$")
+
+func needsQuotes(s string) bool {
+ if indexInSlice(dotKeywords, s) != -1 {
+ return false
+ }
+ if alreadyQuotedRegex.MatchString(s) {
+ return false
+ }
+ if validIdentifierRegexWithPort.MatchString(s) || validIdentifierRegex.MatchString(s) {
+ return false
+ }
+
+ return true
+}
+
+func QuoteIfNecessary(s string) (result string) {
+ if needsQuotes(s) {
+ s = strings.Replace(s, "\"", "\\\"", -1)
+ s = strings.Replace(s, "\n", "\\n", -1)
+ s = strings.Replace(s, "\r", "\\r", -1)
+ s = "\"" + s + "\""
+ }
+ return s
+}
+
+func validAttribute(attributeCollection []string, attributeName string) bool {
+ return indexInSlice(attributeCollection, attributeName) != -1
+}
+
+func validGraphAttribute(attributeName string) bool {
+ return validAttribute(graphAttributes, attributeName)
+}
+
+func validNodeAttribute(attributeName string) bool {
+ return validAttribute(nodeAttributes, attributeName)
+}
+
+func sortedKeys(sourceMap map[string]string) []string {
+ keys := make([]string, 0, len(sourceMap))
+ for k, _ := range sourceMap {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+func (gt GraphType) String() string {
+ if gt == DIGRAPH {
+ return "digraph"
+ } else if gt == GRAPH {
+ return "graph"
+ } else if gt == SUBGRAPH {
+ return "subgraph"
+ }
+ return "(invalid)"
+}
+
+func (c *common) Type() string {
+ return c._type
+}
+
+func (c *common) GetParentGraph() *Graph {
+ return c.parentGraph
+}
+
+func (c *common) SetParentGraph(g *Graph) {
+ c.parentGraph = g
+}
+
+func (c *common) Sequence() int {
+ return c.sequence
+}
+
+func (c *common) Get(attributeName string) string {
+ return c.attributes[attributeName]
+}
+
+func (c *common) Set(attributeName, attributeValue string) error {
+ c.attributes[attributeName] = attributeValue
+ return nil
+}
+
+func setAttribute(validAttributes []string, attributes map[string]string, attributeName, attributeValue string) error {
+ if validAttribute(validAttributes, attributeName) {
+ attributes[attributeName] = attributeValue
+ return nil
+ }
+ return AttributeError
+}
+
+func (g *Graph) Set(attributeName, attributeValue string) error {
+ return setAttribute(graphAttributes, g.common.attributes, attributeName, attributeValue)
+}
+
+func (g *Graph) SetGlobalNodeAttr(attributeName, attributeValue string) error {
+ return setAttribute(nodeAttributes, g.nodeAttributes, attributeName, attributeValue)
+}
+
+func (g *Graph) SetGlobalEdgeAttr(attributeName, attributeValue string) error {
+ return setAttribute(edgeAttributes, g.edgeAttributes, attributeName, attributeValue)
+}
+
+func (n *Node) Set(attributeName, attributeValue string) error {
+ return setAttribute(nodeAttributes, n.common.attributes, attributeName, attributeValue)
+}
+
+func (e *Edge) Set(attributeName, attributeValue string) error {
+ return setAttribute(edgeAttributes, e.common.attributes, attributeName, attributeValue)
+}
+
+func (c *common) setSequence(sequence int) {
+ c.sequence = sequence
+}
+
+// SameRank enforces alignment of the given nodes
+func (g *Graph) SameRank(nodes []string) {
+ g.sameRank = append(g.sameRank, nodes)
+}
+
+// Set the type of the graph, valid values are GRAPH or DIGRAPH
+func (g *Graph) SetType(t GraphType) {
+ g.graphType = t
+ // @todo consider disallowing setting type to SUBGRAPH
+}
+
+func (c common) Name() string {
+ return c.name
+}
+
+func (g *Graph) GetRoot() (result *Graph) {
+ result = g
+ for parent := g.GetParentGraph(); parent != result; parent = parent.GetParentGraph() {
+ result = parent
+ }
+ return result
+}
+
+func (g *Graph) getNextSequenceNumber() (next int) {
+ next = g.currentChildSequence
+ g.currentChildSequence += 1
+ return
+}
+func (g *Graph) AddNode(n *Node) {
+ name := n.Name()
+ if _, ok := g.nodes[name]; !ok {
+ g.nodes[name] = make([]*Node, 0)
+ }
+ n.setSequence(g.getNextSequenceNumber())
+ n.SetParentGraph(g.GetParentGraph())
+ g.nodes[name] = append(g.nodes[name], n)
+}
+
+func (g *Graph) AddEdge(e *Edge) {
+ name := e.Name()
+ if _, ok := g.edges[name]; !ok {
+ g.edges[name] = make([]*Edge, 0)
+ }
+ e.setSequence(g.getNextSequenceNumber())
+ e.SetParentGraph(g.GetParentGraph())
+ g.edges[name] = append(g.edges[name], e)
+}
+
+func (g *Graph) AddSubgraph(sg *SubGraph) {
+ name := sg.Name()
+ if _, ok := g.subgraphs[name]; !ok {
+ g.subgraphs[name] = make([]*SubGraph, 0)
+ }
+ sg.setSequence(g.getNextSequenceNumber())
+ g.subgraphs[name] = append(g.subgraphs[name], sg)
+}
+
+func (g *Graph) GetSubgraphs() (result []*SubGraph) {
+ result = make([]*SubGraph, 0)
+ for _, sgs := range g.subgraphs {
+ for _, sg := range sgs {
+ result = append(result, sg)
+ }
+ }
+ return result
+}
+
+func (g Graph) String() string {
+ var parts []string
+ if g.strict {
+ parts = append(parts, "strict ")
+ }
+ if g.name == "" {
+ parts = append(parts, "{\n")
+ } else {
+ parts = append(parts, fmt.Sprintf("%s %s {\n", g.graphType, QuoteIfNecessary(g.name)))
+ }
+
+ if len(g.attributes) > 0 {
+ attrs := make([]string, 0, len(g.attributes))
+ for _, key := range sortedKeys(g.attributes) {
+ attrs = append(attrs, " "+key+"="+QuoteIfNecessary(g.attributes[key]))
+ }
+ if len(attrs) > 0 {
+ parts = append(parts, "graph [\n")
+ parts = append(parts, strings.Join(attrs, ";\n"))
+ parts = append(parts, ";\n];\n")
+ }
+ }
+
+ if len(g.nodeAttributes) > 0 {
+ attrs := make([]string, 0, len(g.nodeAttributes))
+ for _, key := range sortedKeys(g.nodeAttributes) {
+ attrs = append(attrs, " "+key+"="+QuoteIfNecessary(g.nodeAttributes[key]))
+ }
+ if len(attrs) > 0 {
+ parts = append(parts, "node [\n")
+ parts = append(parts, strings.Join(attrs, ";\n"))
+ parts = append(parts, ";\n];\n")
+ }
+ }
+
+ if len(g.edgeAttributes) > 0 {
+ attrs := make([]string, 0, len(g.edgeAttributes))
+ for _, key := range sortedKeys(g.edgeAttributes) {
+ attrs = append(attrs, " "+key+"="+QuoteIfNecessary(g.edgeAttributes[key]))
+ }
+ if len(attrs) > 0 {
+ parts = append(parts, "edge [\n")
+ parts = append(parts, strings.Join(attrs, ";\n"))
+ parts = append(parts, ";\n];\n")
+ }
+ }
+
+ objectList := make(graphObjects, 0)
+
+ for _, nodes := range g.nodes {
+ for _, node := range nodes {
+ objectList = append(objectList, node)
+ }
+ }
+ for _, edges := range g.edges {
+ for _, edge := range edges {
+ objectList = append(objectList, edge)
+ }
+ }
+ for _, subgraphs := range g.subgraphs {
+ for _, subgraph := range subgraphs {
+ objectList = append(objectList, subgraph)
+ }
+ }
+ sort.Sort(objectList)
+
+ for _, obj := range objectList {
+ //@todo type-based decision making re: supressDisconnected and simplify
+ //switch o := obj.(type) {
+ //case *Node:
+ //}
+ parts = append(parts, fmt.Sprintf("%s\n", obj))
+ }
+
+ for _, nodes := range g.sameRank {
+ parts = append(parts, fmt.Sprintf("{ rank=same %s }", strings.Join(nodes, " ")))
+ }
+
+ parts = append(parts, "}\n")
+ return strings.Join(parts, "")
+}
+
+type Node struct {
+ common
+}
+
+func NewNode(name string) *Node {
+ return &Node{
+ common{
+ name: name,
+ attributes: make(map[string]string, 0),
+ },
+ }
+}
+
+func (n Node) String() string {
+
+ name := QuoteIfNecessary(n.name)
+
+ parts := make([]string, 0)
+
+ attrs := make([]string, 0)
+ for _, key := range sortedKeys(n.attributes) {
+ value := n.attributes[key]
+ if key == "label" && len(value) > 4 && value[0] == '<' && value[len(value)-1] == '>' {
+ attrs = append(attrs, key+"="+value)
+ } else {
+ attrs = append(attrs, key+"="+QuoteIfNecessary(value))
+ }
+ }
+ if len(attrs) > 0 {
+ parts = append(parts, strings.Join(attrs, ", "))
+ }
+
+ //@todo don't print if node is empty
+ if len(parts) > 0 {
+ name += " [" + strings.Join(parts, ", ") + "]"
+ }
+
+ return name + ";"
+}
+
+type Edge struct {
+ common
+ points [2]*Node
+}
+
+func NewEdge(src, dst *Node) *Edge {
+ return &Edge{
+ common{
+ _type: "edge",
+ attributes: make(map[string]string, 0),
+ },
+ [2]*Node{src, dst},
+ }
+}
+
+func (e Edge) Source() *Node {
+ return e.points[0]
+}
+
+func (e Edge) Destination() *Node {
+ return e.points[1]
+}
+
+func (e Edge) String() string {
+ src, dst := e.Source(), e.Destination()
+ parts := make([]string, 0)
+
+ parts = append(parts, QuoteIfNecessary(src.Name()))
+
+ parent := e.GetParentGraph()
+ if parent != nil && parent.GetRoot() != nil && parent.GetRoot().graphType == DIGRAPH {
+ parts = append(parts, "->")
+ } else {
+ parts = append(parts, "--")
+ }
+ parts = append(parts, QuoteIfNecessary(dst.Name()))
+
+ attrs := make([]string, 0)
+ for _, key := range sortedKeys(e.attributes) {
+ attrs = append(attrs, key+"="+QuoteIfNecessary(e.attributes[key]))
+ }
+ if len(attrs) > 0 {
+ parts = append(parts, " [")
+ parts = append(parts, strings.Join(attrs, ", "))
+ parts = append(parts, "]")
+ }
+
+ return strings.Join(parts, " ")
+}
+
+func init() {
+ sort.Strings(graphAttributes)
+ sort.Strings(nodeAttributes)
+ sort.Strings(edgeAttributes)
+ sort.Strings(clusterAttributes)
+}
diff --git a/utils/dag/dot/dot_test.go b/utils/dag/dot/dot_test.go
new file mode 100644
index 000000000..431fd0db8
--- /dev/null
+++ b/utils/dag/dot/dot_test.go
@@ -0,0 +1,151 @@
+package dot_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/Fantom-foundation/go-opera/utils/dag/dot"
+)
+
+func TestQuotingIfNecessary(t *testing.T) {
+ cases := map[string]string{
+ "foo": "foo",
+ "\"foo\"": "\"foo\"",
+ "foo bar": "\"foo bar\"",
+ "Allen, C.": "\"Allen, C.\"",
+ }
+
+ for input, expected := range cases {
+ if dot.QuoteIfNecessary(input) != expected {
+ t.Errorf("'%s' != '%s'", dot.QuoteIfNecessary(input), expected)
+ }
+ }
+}
+
+func TestGraphPrinting(t *testing.T) {
+ g1 := dot.NewGraph("foo")
+ expected1 := "digraph foo {\n}\n"
+ g2 := dot.NewGraph("foo bar")
+ expected2 := "digraph \"foo bar\" {\n}\n"
+
+ if fmt.Sprint(g1) != expected1 {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(g1), expected1)
+ }
+ if fmt.Sprint(g2) != expected2 {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(g2), expected2)
+ }
+}
+
+func TestCreateSimpleGraphWithNode(t *testing.T) {
+ g := dot.NewGraph("Test")
+
+ expected := "digraph Test {\n}\n"
+ if fmt.Sprint(g) != expected {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(g), expected)
+ }
+ g.SetType(dot.GRAPH)
+
+ expected = "graph Test {\n}\n"
+ if fmt.Sprint(g) != expected {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(g), expected)
+ }
+ g.SetType(dot.DIGRAPH)
+
+ node := dot.NewNode("legend")
+ node.Set("shape", "box")
+ g.AddNode(node)
+ node.Set("label", "value with spaces")
+
+ node = dot.NewNode("html")
+ node.Set("shape", "plain")
+ node.Set("label", "<bold>")
+ g.AddNode(node)
+
+ expected = "digraph Test {\nlegend [label=\"value with spaces\", shape=box];\nhtml [label=<bold>, shape=plain];\n}\n"
+ if fmt.Sprint(g) != expected {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(g), expected)
+ }
+}
+
+func TestCreateSimpleNode(t *testing.T) {
+ node := dot.NewNode("nodename")
+ node.Set("shape", "box")
+ node.Set("label", "mine")
+
+ expected := "nodename [label=mine, shape=box];"
+ if fmt.Sprint(node) != expected {
+ t.Errorf("'%s' != '%s'", fmt.Sprint(node), expected)
+ }
+}
+
+func TestGraphAttributeSetting(t *testing.T) {
+ g := dot.NewGraph("Test")
+ if g.Set("label", "foo") != nil {
+ t.Error("Error setting value on g", g)
+ }
+ g.Set("Damping", "x")
+ if g.Set("this_does_not_exist", "and_should_error") != dot.AttributeError {
+ t.Error("Did not get godot.AttributeError when setting invalid attribute on g", g)
+ }
+}
+
+func TestSubGraphs(t *testing.T) {
+ g := dot.NewGraph("G")
+ s := dot.NewSubgraph("SG")
+
+ subgraphs := make([]*dot.SubGraph, 0)
+ if subgraphs = g.GetSubgraphs(); len(subgraphs) != 0 {
+ t.Error("Non-empty subgraphs returned:", subgraphs)
+ }
+ g.AddSubgraph(s)
+ if g.GetSubgraphs()[0].Name() != s.Name() {
+ t.Error(g.GetSubgraphs()[0].Name(), " != ", s.Name())
+ }
+
+ expected := `digraph G {
+subgraph SG {
+}
+
+}
+`
+
+ if fmt.Sprint(g) != expected {
+ t.Errorf("'%s' != '%s'", g, expected)
+ }
+}
+
+func TestEdgeAddition(t *testing.T) {
+ simple_graph := `digraph G {
+graph [
+ label="this is a graph";
+];
+a -> b
+}
+`
+ g := dot.NewGraph("G")
+ g.Set("label", "this is a graph")
+ a, b := dot.NewNode("a"), dot.NewNode("b")
+ e := dot.NewEdge(a, b)
+ g.AddEdge(e)
+
+ if fmt.Sprint(g) != simple_graph {
+ t.Errorf("'%s' != '%s'", g, simple_graph)
+ }
+
+}
+
+func TestQuoting(t *testing.T) {
+ g := dot.NewGraph("G")
+ a, b := dot.NewNode("192.168.1.1"), dot.NewNode("192.168.1.2")
+ e := dot.NewEdge(a, b)
+ g.AddEdge(e)
+
+ expected := `digraph G {
+"192.168.1.1" -> "192.168.1.2"
+}
+`
+ if fmt.Sprint(g) != expected {
+ t.Errorf("'%s' != '%s'", g, expected)
+ }
+
+}
diff --git a/utils/dag/dot/example_test.go b/utils/dag/dot/example_test.go
new file mode 100644
index 000000000..3f2dd9f66
--- /dev/null
+++ b/utils/dag/dot/example_test.go
@@ -0,0 +1,34 @@
+package dot_test
+
+import (
+ "fmt"
+
+ "github.com/Fantom-foundation/go-opera/utils/dag/dot"
+)
+
+func ExampleNewGraph() {
+ g := dot.NewGraph("G")
+ g.Set("label", "Example graph")
+ n1, n2 := dot.NewNode("Node1"), dot.NewNode("Node2")
+
+ n1.Set("color", "sienna")
+
+ g.AddNode(n1)
+ g.AddNode(n2)
+
+ e := dot.NewEdge(n1, n2)
+ e.Set("dir", "both")
+ g.AddEdge(e)
+
+ fmt.Println(g)
+ // Output:
+ // digraph G {
+ // graph [
+ // label="Example graph";
+ // ];
+ // Node1 [color=sienna];
+ // Node2;
+ // Node1 -> Node2 [ dir=both ]
+ // }
+ //
+}
diff --git a/utils/dag/graph_inmem.go b/utils/dag/graph_inmem.go
index 0a0cc678c..84a03beae 100644
--- a/utils/dag/graph_inmem.go
+++ b/utils/dag/graph_inmem.go
@@ -3,6 +3,7 @@ package dag
import (
"fmt"
"math"
+ "strings"
"github.com/Fantom-foundation/lachesis-base/abft"
"github.com/Fantom-foundation/lachesis-base/gossip/dagordering"
@@ -11,79 +12,19 @@ import (
"github.com/Fantom-foundation/lachesis-base/inter/idx"
"github.com/Fantom-foundation/lachesis-base/kvdb/memorydb"
"github.com/ethereum/go-ethereum/log"
- "gonum.org/v1/gonum/graph"
- "gonum.org/v1/gonum/graph/encoding"
- "gonum.org/v1/gonum/graph/encoding/dot"
"github.com/Fantom-foundation/go-opera/gossip"
"github.com/Fantom-foundation/go-opera/integration"
"github.com/Fantom-foundation/go-opera/inter"
"github.com/Fantom-foundation/go-opera/inter/iblockproc"
"github.com/Fantom-foundation/go-opera/utils/adapters/vecmt2dagidx"
+ "github.com/Fantom-foundation/go-opera/utils/dag/dot"
"github.com/Fantom-foundation/go-opera/vecmt"
)
-type eventSeq []hash.Event
-
-func (s *eventSeq) event2id(h hash.Event) int64 {
- id := int64(len(*s))
- *s = append(*s, h)
- return id
-}
-
-func (s *eventSeq) id2event(id int64) hash.Event {
- return (*s)[id]
-}
-
-type eventList map[hash.Event]*dotNode
-
-func (l *eventList) set(n *dotNode) {
- (*l)[n.hash] = n
-}
-
-func (l *eventList) get(h hash.Event) *dotNode {
- return (*l)[h]
-}
-
-// graphInMem implements dot.Graph over inmem refs and nodes
-type graphInMem struct {
- name string
- global bool
- subGraphs map[string]*graphInMem
-
- include map[int64]struct{}
- refs *eventSeq
- nodes *eventList
- attrs struct {
- graph attributer
- edge attributer
- }
-}
-
-func newGraphInMem(name string) *graphInMem {
- return &graphInMem{
- name: name,
- include: make(map[int64]struct{}),
- subGraphs: make(map[string]*graphInMem),
-
- attrs: struct{ graph, edge attributer }{
- attributer(make(map[string]string, 10)),
- attributer(make(map[string]string, 10)),
- },
- }
-}
-
// readDagGraph read gossip.Store into inmem dot.Graph
-func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch) *graphInMem {
- g := newGraphInMem("DOT")
- g.global = true
- g.refs = &eventSeq{}
- g.nodes = &eventList{}
- g.attrs.graph.setAttr("clusterrank", "local")
- g.attrs.graph.setAttr("compound", "true")
- g.attrs.graph.setAttr("newrank", "true")
- g.attrs.graph.setAttr("ranksep", "0.05")
- g.attrs.edge.setAttr("constraint", "true")
+func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch) *dot.Graph {
+ // 0. Set gossip data:
cdb := abft.NewMemStore()
defer cdb.Close()
@@ -107,6 +48,22 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
panic(err)
}
+ // 1. Set dot.Graph data:
+
+ g := dot.NewGraph("DOT")
+ g.Set("clusterrank", "local")
+ g.Set("compound", "true")
+ g.Set("newrank", "true")
+ g.Set("ranksep", "0.05")
+ g.SetGlobalEdgeAttr("constraint", "true")
+
+ var (
+ nodes = make(map[hash.Event]*dot.Node)
+ subGraphs = make(map[idx.ValidatorID]*dot.SubGraph)
+ )
+
+ // 2. Set event processor data:
+
var (
epoch idx.Epoch
prevBS *iblockproc.BlockState
@@ -119,8 +76,8 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
for f := idx.Frame(0); f <= cdb.GetLastDecidedFrame(); f++ {
rr := cdb.GetFrameRoots(f)
for _, r := range rr {
- node := g.nodes.get(r.ID)
- markAsRoot(node)
+ n := nodes[r.ID]
+ markAsRoot(n)
}
}
@@ -131,15 +88,16 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
maxBlock = bs.LastBlock.Idx
}
- for n := prevBS.LastBlock.Idx + 1; n <= maxBlock; n++ {
- block := gdb.GetBlock(n)
+ for b := prevBS.LastBlock.Idx + 1; b <= maxBlock; b++ {
+ block := gdb.GetBlock(b)
if block == nil {
break
}
- node := g.nodes.get(block.Atropos)
- if node != nil {
- markAsAtropos(node)
+ n := nodes[block.Atropos]
+ if n == nil {
+ continue
}
+ markAsAtropos(n)
}
}
@@ -150,7 +108,22 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
validators := gdb.GetHistoryEpochState(epoch).Validators
for _, v := range validators.IDs() {
- _ = g.subGraph(v)
+ if _, ok := subGraphs[v]; ok {
+ continue
+ }
+ group := groupName(v)
+ sg := dot.NewSubgraph(fmt.Sprintf("cluster%d", len(subGraphs)))
+ sg.Set("label", group)
+ sg.Set("sortv", fmt.Sprintf("%d", v))
+ sg.Set("style", "dotted")
+
+ pseudoNode := dot.NewNode(group)
+ pseudoNode.Set("style", "invis")
+ pseudoNode.Set("width", "0")
+ sg.AddNode(pseudoNode)
+
+ subGraphs[v] = sg
+ g.AddSubgraph(sg)
}
processed = make(map[hash.Event]dag.Event, 1000)
@@ -175,8 +148,21 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
dagIndexer.Flush()
orderer.Process(e)
- sg := g.subGraph(e.Creator())
- sg.addDagEvent(e)
+ name := fmt.Sprintf("%s\n%d", e.ID().String(), e.Creator())
+ n := dot.NewNode(name)
+ sg := subGraphs[e.Creator()]
+ sg.AddNode(n)
+ nodes[e.ID()] = n
+
+ for _, h := range e.Parents() {
+ p := nodes[h]
+ ref := dot.NewEdge(n, p)
+ if processed[h].Creator() == e.Creator() {
+ sg.AddEdge(ref)
+ } else {
+ g.AddEdge(ref)
+ }
+ }
return nil
},
@@ -194,7 +180,8 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
},
})
- // process events
+ // 3. Iterate over events:
+
gdb.ForEachEvent(from, func(e *inter.EventPayload) bool {
// current epoch is finished, so process accumulated events
if epoch < e.Epoch() {
@@ -216,230 +203,19 @@ func readDagGraph(gdb *gossip.Store, cfg integration.Configs, from, to idx.Epoch
epoch++
readRestoredAbftStore()
- return g
-}
-
-func (g *graphInMem) DOTID() string {
- return g.name
-}
-
-// Structure returns subgraphs.
-func (g *graphInMem) Structure() []dot.Graph {
- res := make([]dot.Graph, 0, len(g.subGraphs))
- for _, sg := range g.subGraphs {
- res = append(res, sg)
- }
- return res
-}
-
-func (g *graphInMem) subGraph(v idx.ValidatorID) *graphInMem {
- name := fmt.Sprintf("cluster%d", v)
- label := fmt.Sprintf("validator-%d", v)
- sg, ok := g.subGraphs[name]
- if !ok {
- sg = newGraphInMem(name)
- sg.refs = g.refs
- sg.nodes = g.nodes
- sg.attrs.graph.setAttr("label", label)
- sg.attrs.graph.setAttr("sortv", fmt.Sprintf("%d", v))
- sg.attrs.graph.setAttr("style", "dotted")
- sg.attrs.edge.setAttr("constraint", "true")
- g.subGraphs[name] = sg
- sg.addPseudoNode(label)
- }
- return sg
-}
-
-// DOTAttributers are graph.Graph values that specify top-level DOT attributes.
-func (g *graphInMem) DOTAttributers() (graph, node, edge encoding.Attributer) {
- graph = g.attrs.graph
- node = attributer(make(map[string]string, 0)) // empty
- edge = g.attrs.edge
- return
-}
-
-func (g *graphInMem) addDagEvent(e dag.Event) (id int64) {
- n := g.nodes.get(e.ID())
- if n == nil {
- id = g.refs.event2id(e.ID())
- n = newDotNode(id, e)
- g.nodes.set(n)
- } else {
- id = n.id
- }
-
- g.include[id] = struct{}{}
- return
-}
-
-func (g *graphInMem) addPseudoNode(name string) (id int64) {
- h := hash.BytesToEvent([]byte(name))
- n := g.nodes.get(h)
- if n == nil {
- id = g.refs.event2id(h)
- n = &dotNode{
- id: id,
- hash: h,
- attributer: attributer(make(map[string]string, 10)),
- }
- n.setAttr("label", name)
- n.setAttr("style", "invis")
- n.setAttr("width", "0")
- g.nodes.set(n)
- } else {
- id = n.id
- }
-
- g.include[id] = struct{}{}
- return
-}
-
-// Node returns the node with the given ID if it exists
-// in the graph, and nil otherwise.
-func (g *graphInMem) Node(id int64) graph.Node {
- if _, ok := g.include[id]; !ok {
- return nil
- }
- hash := g.refs.id2event(id)
- return g.nodes.get(hash)
-}
+ // 4. Result
-// Nodes returns all the nodes in the graph.
-//
-// Nodes must not return nil.
-func (g *graphInMem) Nodes() graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
-
- go func() {
- defer close(nn.data)
-
- for id := range g.include {
- h := g.refs.id2event(id)
- e := g.nodes.get(h)
- nn.data <- e
- }
- }()
-
- return nn
-}
-
-// From returns all nodes that can be reached directly
-// from the node with the given ID.
-//
-// From must not return nil.
-func (g *graphInMem) From(id int64) graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
-
- h := g.refs.id2event(id)
- x := g.nodes.get(h)
-
- go func() {
- defer close(nn.data)
- for _, p := range x.parents {
- y := g.nodes.get(p)
- if g.hasEdge(x, y) {
- nn.data <- y
- }
- }
- }()
-
- return nn
-}
-
-// To returns all nodes that can reach directly
-// to the node with the given ID.
-//
-// To must not return nil.
-func (g *graphInMem) To(id int64) graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
- close(nn.data)
- return nn
-}
-
-// HasEdgeBetween returns whether an edge exists between
-// nodes with IDs xid and yid without considering direction.
-func (g *graphInMem) HasEdgeBetween(xid, yid int64) bool {
- x := g.nodes.get(g.refs.id2event(xid))
- y := g.nodes.get(g.refs.id2event(yid))
- if !g.hasEdge(x, y) {
- return false
- }
-
- for _, p := range x.parents {
- if p == y.hash {
- return true
- }
- }
- for _, p := range y.parents {
- if p == x.hash {
- return true
- }
- }
-
- return false
-}
-
-// HasEdgeFromTo returns whether an edge exists
-// in the graph from u to v with IDs uid and vid.
-func (g *graphInMem) HasEdgeFromTo(uid, vid int64) bool {
- u := g.nodes.get(g.refs.id2event(uid))
- v := g.nodes.get(g.refs.id2event(vid))
- if !g.hasEdge(u, v) {
- return false
- }
-
- for _, p := range u.parents {
- if p == v.hash {
- return true
- }
- }
-
- return false
-}
-
-// Edge returns the edge from u to v, with IDs uid and vid,
-// if such an edge exists and nil otherwise. The node v
-// must be directly reachable from u as defined by the
-// From method.
-func (g *graphInMem) Edge(uid, vid int64) graph.Edge {
- u := g.nodes.get(g.refs.id2event(uid))
- v := g.nodes.get(g.refs.id2event(vid))
- if !g.hasEdge(u, v) {
- return nil
- }
-
- for _, p := range u.parents {
- if p == v.hash {
- e := &dotEdge{
- x: u,
- y: v,
- }
- return e
- }
- }
-
- return nil
-}
-
-func (g *graphInMem) hasEdge(x, y *dotNode) bool {
- if _, ok := g.include[x.id]; !ok && !g.global {
- return false
- }
- if _, ok := g.include[y.id]; !ok && !g.global {
- return false
- }
-
- if g.global && x.creator == y.creator {
- return false
+ // NOTE: github.com/tmc/dot renders subgraphs not in the ordering that specified
+ // so we introduce pseudo nodes and edges to work around
+ groups := make([]string, 0, len(subGraphs))
+ for v := range subGraphs {
+ groups = append(groups, groupName(v))
}
+ g.SameRank([]string{
+ "\"" + strings.Join(groups, `" -> "`) + "\" [style = invis, constraint = true];",
+ })
- return true
+ return g
}
func panics(name string) func(error) {
@@ -448,14 +224,18 @@ func panics(name string) func(error) {
}
}
-func markAsRoot(n *dotNode) {
+func markAsRoot(n *dot.Node) {
// n.setAttr("xlabel", "root")
- n.setAttr("style", "filled")
- n.setAttr("fillcolor", "#FFFF00")
+ n.Set("style", "filled")
+ n.Set("fillcolor", "#FFFF00")
}
-func markAsAtropos(n *dotNode) {
+func markAsAtropos(n *dot.Node) {
// n.setAttr("xlabel", "atropos")
- n.setAttr("style", "filled")
- n.setAttr("fillcolor", "#FF0000")
+ n.Set("style", "filled")
+ n.Set("fillcolor", "#FF0000")
+}
+
+func groupName(v idx.ValidatorID) string {
+ return fmt.Sprintf("host-%d", v)
}
diff --git a/utils/dag/graph_ondisk.go b/utils/dag/graph_ondisk.go
deleted file mode 100644
index daff459e3..000000000
--- a/utils/dag/graph_ondisk.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package dag
-
-import (
- "github.com/Fantom-foundation/lachesis-base/hash"
- "github.com/Fantom-foundation/lachesis-base/inter/idx"
- "gonum.org/v1/gonum/graph"
-
- "github.com/Fantom-foundation/go-opera/gossip"
- "github.com/Fantom-foundation/go-opera/inter"
-)
-
-// dagReader implements dot.Graph over gossip.Store
-type graphOnDisk struct {
- db *gossip.Store
- epochFrom idx.Epoch
- epochTo idx.Epoch
-}
-
-func (g *graphOnDisk) DOTID() string {
- return "DAG"
-}
-
-// Node returns the node with the given ID if it exists
-// in the graph, and nil otherwise.
-func (g *graphOnDisk) Node(id int64) graph.Node {
- e := g.db.GetEvent(id2event(id))
- return newDotNode(id, e)
-}
-
-// Nodes returns all the nodes in the graph.
-//
-// Nodes must not return nil.
-func (g *graphOnDisk) Nodes() graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
-
- go func() {
- defer close(nn.data)
- g.db.ForEachEvent(g.epochFrom, func(e *inter.EventPayload) bool {
- if g.epochTo >= g.epochFrom && e.Epoch() > g.epochTo {
- return false
- }
-
- id := event2id(e.ID())
- nn.data <- newDotNode(id, &e.Event)
- return true
- })
- }()
-
- return nn
-}
-
-// From returns all nodes that can be reached directly
-// from the node with the given ID.
-//
-// From must not return nil.
-func (g *graphOnDisk) From(id int64) graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
-
- x := g.Node(id).(*dotNode)
- go func() {
- defer close(nn.data)
- for _, p := range x.parents {
- n := g.Node(event2id(p))
- nn.data <- n.(*dotNode)
- }
- }()
-
- return nn
-}
-
-// To returns all nodes that can reach directly
-// to the node with the given ID.
-//
-// To must not return nil.
-func (g *graphOnDisk) To(id int64) graph.Nodes {
- nn := &dagNodes{
- data: make(chan *dotNode),
- }
- close(nn.data)
- return nn
-}
-
-// HasEdgeBetween returns whether an edge exists between
-// nodes with IDs xid and yid without considering direction.
-func (g *graphOnDisk) HasEdgeBetween(xid, yid int64) bool {
- x := g.Node(xid).(*dotNode)
- y := g.Node(yid).(*dotNode)
-
- for _, p := range x.parents {
- if p == y.hash {
- return true
- }
- }
- for _, p := range y.parents {
- if p == x.hash {
- return true
- }
- }
-
- return false
-}
-
-// HasEdgeFromTo returns whether an edge exists
-// in the graph from u to v with IDs uid and vid.
-func (g *graphOnDisk) HasEdgeFromTo(uid, vid int64) bool {
- u := g.Node(uid).(*dotNode)
- v := g.Node(vid).(*dotNode)
-
- for _, p := range u.parents {
- if p == v.hash {
- return true
- }
- }
-
- return false
-}
-
-// Edge returns the edge from u to v, with IDs uid and vid,
-// if such an edge exists and nil otherwise. The node v
-// must be directly reachable from u as defined by the
-// From method.
-func (g *graphOnDisk) Edge(uid, vid int64) graph.Edge {
- u := g.Node(uid).(*dotNode)
- v := g.Node(vid).(*dotNode)
-
- for _, p := range u.parents {
- if p == v.hash {
- return &dotEdge{
- x: u,
- y: v,
- }
- }
- }
-
- return nil
-}
-
-// --
-
-var (
- id2hash = make(map[int64]hash.Event)
-)
-
-func id2event(id int64) hash.Event {
- return id2hash[id]
-}
-
-func event2id(e hash.Event) int64 {
- // NOTE: possible collision
- var id int64
- for i := 0; i < 8; i++ {
- id += int64(e[8+i] << (8 * i))
- }
-
- id2hash[id] = e
-
- return id
-}