Skip to content
Snippets Groups Projects
Commit 6c0a0f02 authored by Hans Moog's avatar Hans Moog
Browse files

Feat: refactored ledger state + visualizer

parent 25fed2e5
No related branches found
No related tags found
No related merge requests found
package ledgerstate
import (
"io/ioutil"
"os"
"os/exec"
"reflect"
"sort"
"strconv"
......@@ -234,68 +231,6 @@ func (ledgerState *LedgerState) GenerateRealityVisualization(pngFilename string)
return graphviz.RenderPNG(graph, pngFilename)
}
func (ledgerState *LedgerState) GenerateVisualization() error {
di := dot.NewGraph(dot.Directed)
realityGraphs := make(map[RealityId]*dot.Graph)
getRealityGraph := func(realityId RealityId) *dot.Graph {
realityGraph, exists := realityGraphs[realityId]
if !exists {
realityGraph = di.Subgraph("REALITY: "+strings.Trim(realityId.String(), "\x00"), dot.ClusterOption{})
realityGraph.Attr("style", "filled")
realityGraph.Attr("color", "lightgrey")
realityGraph.Attr("fillcolor", "lightgrey")
realityGraphs[realityId] = realityGraph
}
return realityGraph
}
var drawTransferOutput func(transferOutput *TransferOutput) dot.Node
drawTransferOutput = func(transferOutput *TransferOutput) dot.Node {
transferOutputNode := getRealityGraph(transferOutput.GetRealityId()).Node(transferOutput.GetTransferHash().String() + "\n" + transferOutput.GetAddressHash().String())
transferOutputNode.Attr("style", "filled")
transferOutputNode.Attr("color", "white")
transferOutputNode.Attr("fillcolor", "white")
for transferHash, addresses := range transferOutput.GetConsumers() {
for _, addressHash := range addresses {
ledgerState.GetTransferOutput(NewTransferOutputReference(transferHash, addressHash)).Consume(func(object objectstorage.StorableObject) {
transferOutputNode.Edge(drawTransferOutput(object.(*TransferOutput)))
})
}
}
return transferOutputNode
}
ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
drawTransferOutput(object.(*TransferOutput))
})
return true
}, MAIN_REALITY_ID)
d1 := []byte(di.String())
if err := ioutil.WriteFile("_tmp.dot", d1, 0755); err != nil {
return err
}
_, err := exec.Command("dot", "-Tpng", "_tmp.dot", "-o", "viz.png").Output()
if err != nil {
return err
}
if err := os.Remove("_tmp.dot"); err != nil {
return err
}
return nil
}
func (ledgerState *LedgerState) AggregateRealities(realityIds ...RealityId) *objectstorage.CachedObject {
switch len(realityIds) {
case 0:
......
......@@ -8,13 +8,11 @@ import (
"github.com/iotaledger/hive.go/parameter"
"github.com/iotaledger/goshimmer/packages/utils"
"github.com/iotaledger/hive.go/objectstorage"
)
var (
iota = NewColor("IOTA")
iota_ = NewColor("IOTA")
eth = NewColor("ETH")
transferHash1 = NewTransferHash("TRANSFER1")
transferHash2 = NewTransferHash("TRANSFER2")
......@@ -63,7 +61,7 @@ func Benchmark(b *testing.B) {
func Test(t *testing.T) {
ledgerState := NewLedgerState("testLedger").Prune().AddTransferOutput(
transferHash1, addressHash1, NewColoredBalance(eth, 1337), NewColoredBalance(iota, 1338),
transferHash1, addressHash1, NewColoredBalance(eth, 1337), NewColoredBalance(iota_, 1338),
)
ledgerState.CreateReality(pendingReality)
......@@ -71,11 +69,11 @@ func Test(t *testing.T) {
transfer := NewTransfer(transferHash2).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash3, NewColoredBalance(iota, 338),
addressHash3, NewColoredBalance(iota_, 338),
).AddOutput(
addressHash3, NewColoredBalance(eth, 337),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1000),
addressHash4, NewColoredBalance(iota_, 1000),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1000),
)
......@@ -87,11 +85,11 @@ func Test(t *testing.T) {
if err := ledgerState.BookTransfer(NewTransfer(transferHash3).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash3, NewColoredBalance(iota, 338),
addressHash3, NewColoredBalance(iota_, 338),
).AddOutput(
addressHash3, NewColoredBalance(eth, 337),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1000),
addressHash4, NewColoredBalance(iota_, 1000),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1000),
)); err != nil {
......@@ -111,12 +109,20 @@ func Test(t *testing.T) {
})
}
var transferHashCounter = 0
func generateRandomTransferHash() TransferHash {
return NewTransferHash(utils.RandomString(32))
transferHashCounter++
return NewTransferHash("TRANSFER" + strconv.Itoa(transferHashCounter))
}
var addressHashCounter = 0
func generateRandomAddressHash() AddressHash {
return NewAddressHash(utils.RandomString(32))
addressHashCounter++
return NewAddressHash("ADDRESS" + strconv.Itoa(addressHashCounter))
}
func initializeLedgerStateWithBalances(numberOfBalances int) (ledgerState *LedgerState, result []*TransferOutputReference) {
......@@ -126,7 +132,7 @@ func initializeLedgerStateWithBalances(numberOfBalances int) (ledgerState *Ledge
transferHash := generateRandomTransferHash()
addressHash := generateRandomAddressHash()
ledgerState.AddTransferOutput(transferHash, addressHash, NewColoredBalance(iota, 1337))
ledgerState.AddTransferOutput(transferHash, addressHash, NewColoredBalance(iota_, 1337))
result = append(result, NewTransferOutputReference(transferHash, addressHash))
}
......@@ -147,7 +153,27 @@ func spend(ledgerState *LedgerState, transferOutputReferences ...*TransferOutput
addressHash := generateRandomAddressHash()
transfer := NewTransfer(transferHash).AddOutput(
addressHash, NewColoredBalance(iota, uint64(len(transferOutputReferences))*1337),
addressHash, NewColoredBalance(iota_, uint64(len(transferOutputReferences))*1337),
)
for _, transferOutputReference := range transferOutputReferences {
transfer.AddInput(transferOutputReference)
}
if err := ledgerState.BookTransfer(transfer); err != nil {
panic(err)
}
result = NewTransferOutputReference(transferHash, addressHash)
return
}
func spend2(ledgerState *LedgerState, transferOutputReferences ...*TransferOutputReference) (result *TransferOutputReference) {
transferHash := generateRandomTransferHash()
addressHash := generateRandomAddressHash()
transfer := NewTransfer(transferHash).AddOutput(
addressHash, NewColoredBalance(iota_, uint64(len(transferOutputReferences))*2*1337),
)
for _, transferOutputReference := range transferOutputReferences {
transfer.AddInput(transferOutputReference)
......@@ -163,21 +189,29 @@ func spend(ledgerState *LedgerState, transferOutputReferences ...*TransferOutput
}
func TestElevateAggregatedReality(t *testing.T) {
ledgerState, transferOutputs := initializeLedgerStateWithBalances(2)
ledgerState, transferOutputs := initializeLedgerStateWithBalances(3)
// create 2 double spends
doubleSpentOutputs1 := doubleSpend(ledgerState, transferOutputs[0])
doubleSpentOutputs2 := doubleSpend(ledgerState, transferOutputs[1])
normalSpend := spend(ledgerState, transferOutputs[2])
_ = doubleSpend(ledgerState, normalSpend)
// send funds from one of the double spends further
spentInput := spend(ledgerState, doubleSpentOutputs1[1])
// aggregate further sent funds with other reality
spend(ledgerState, spentInput, doubleSpentOutputs2[0])
outputOfAggregatedReality := spend(ledgerState, spentInput, doubleSpentOutputs2[0])
// double spend further spend to elevate aggregated reality
spend(ledgerState, doubleSpentOutputs1[1])
// double spend funds of aggregated reality
spend(ledgerState, spentInput, doubleSpentOutputs2[0])
// spend funds of conflict in aggregated reality further
spend2(ledgerState, outputOfAggregatedReality)
time.Sleep(1000 * time.Millisecond)
objectstorage.WaitForWritesToFlush()
......@@ -186,25 +220,21 @@ func TestElevateAggregatedReality(t *testing.T) {
t.Error(err)
}
ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
fmt.Println(object.(*TransferOutput))
})
return true
}, MAIN_REALITY_ID)
if err := NewVisualizer(ledgerState).RenderTransferOutputs("outputs.png"); err != nil {
t.Error(err)
}
}
func TestElevate(t *testing.T) {
ledgerState := NewLedgerState("testLedger").Prune().AddTransferOutput(
transferHash1, addressHash1, NewColoredBalance(eth, 1337), NewColoredBalance(iota, 1338),
transferHash1, addressHash1, NewColoredBalance(eth, 1337), NewColoredBalance(iota_, 1338),
)
// create first legit spend
if err := ledgerState.BookTransfer(NewTransfer(transferHash2).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash2, NewColoredBalance(iota, 1338),
addressHash2, NewColoredBalance(iota_, 1338),
).AddOutput(
addressHash2, NewColoredBalance(eth, 1337),
)); err != nil {
......@@ -215,7 +245,7 @@ func TestElevate(t *testing.T) {
if err := ledgerState.BookTransfer(NewTransfer(transferHash3).AddInput(
NewTransferOutputReference(transferHash2, addressHash2),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1338),
addressHash4, NewColoredBalance(iota_, 1338),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1337),
)); err != nil {
......@@ -225,7 +255,7 @@ func TestElevate(t *testing.T) {
if err := ledgerState.BookTransfer(NewTransfer(transferHash4).AddInput(
NewTransferOutputReference(transferHash2, addressHash2),
).AddOutput(
addressHash4, NewColoredBalance(iota, 1338),
addressHash4, NewColoredBalance(iota_, 1338),
).AddOutput(
addressHash4, NewColoredBalance(eth, 1337),
)); err != nil {
......@@ -238,7 +268,7 @@ func TestElevate(t *testing.T) {
).AddInput(
NewTransferOutputReference(transferHash4, addressHash4),
).AddOutput(
addressHash6, NewColoredBalance(iota, 2676),
addressHash6, NewColoredBalance(iota_, 2676),
).AddOutput(
addressHash6, NewColoredBalance(eth, 2674),
)); err != nil {
......@@ -249,7 +279,7 @@ func TestElevate(t *testing.T) {
if err := ledgerState.BookTransfer(NewTransfer(transferHash5).AddInput(
NewTransferOutputReference(transferHash1, addressHash1),
).AddOutput(
addressHash5, NewColoredBalance(iota, 1338),
addressHash5, NewColoredBalance(iota_, 1338),
).AddOutput(
addressHash5, NewColoredBalance(eth, 1337),
)); err != nil {
......
packages/ledgerstate/outputs.png

49.4 KiB

packages/ledgerstate/realities.png

67.7 KiB | W: | H:

packages/ledgerstate/realities.png

78.4 KiB | W: | H:

packages/ledgerstate/realities.png
packages/ledgerstate/realities.png
packages/ledgerstate/realities.png
packages/ledgerstate/realities.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -144,7 +144,7 @@ func (reality *Reality) GetAncestorRealities() (result map[RealityId]*objectstor
}
// [DONE] Registers the conflict set in the Reality.
func (reality *Reality) AddConflictSet(conflictSetId ConflictId) {
func (reality *Reality) AddConflict(conflictSetId ConflictId) {
reality.conflictIdsMutex.Lock()
reality.conflictIds[conflictSetId] = void
reality.conflictIdsMutex.Unlock()
......@@ -164,44 +164,47 @@ func (reality *Reality) BookTransfer(transfer *Transfer) (err error) {
return
}
func (reality *Reality) bookTransfer(transferHash TransferHash, inputs objectstorage.CachedObjects, outputs map[AddressHash][]*ColoredBalance) error {
if err := reality.verifyTransfer(inputs, outputs); err != nil {
return err
func (reality *Reality) bookTransfer(transferHash TransferHash, inputs objectstorage.CachedObjects, outputs map[AddressHash][]*ColoredBalance) (err error) {
if err = reality.verifyTransfer(inputs, outputs); err != nil {
return
}
conflictSets, err := reality.consumeInputs(inputs, transferHash, outputs)
conflicts, err := reality.consumeInputs(inputs, transferHash, outputs)
if err != nil {
return err
return
}
if len(conflictSets) >= 1 {
var targetRealityId RealityId
copy(targetRealityId[:], transferHash[:])
if len(conflicts) >= 1 {
targetRealityId := transferHash.ToRealityId()
reality.CreateReality(targetRealityId).Consume(func(object objectstorage.StorableObject) {
targetReality := object.(*Reality)
for _, cachedConflictSet := range conflictSets {
for _, cachedConflictSet := range conflicts {
conflictSet := cachedConflictSet.Get().(*Conflict)
conflictSet.AddReality(targetRealityId)
targetReality.AddConflictSet(conflictSet.GetId())
targetReality.AddConflict(conflictSet.GetId())
}
for addressHash, coloredBalances := range outputs {
targetReality.bookTransferOutput(NewTransferOutput(reality.ledgerState, emptyRealityId, transferHash, addressHash, coloredBalances...))
if err = targetReality.bookTransferOutput(NewTransferOutput(reality.ledgerState, emptyRealityId, transferHash, addressHash, coloredBalances...)); err != nil {
return
}
}
})
} else {
for addressHash, coloredBalances := range outputs {
reality.bookTransferOutput(NewTransferOutput(reality.ledgerState, emptyRealityId, transferHash, addressHash, coloredBalances...))
if err = reality.bookTransferOutput(NewTransferOutput(reality.ledgerState, emptyRealityId, transferHash, addressHash, coloredBalances...)); err != nil {
return
}
}
}
conflictSets.Release()
conflicts.Release()
inputs.Release()
return nil
return
}
// Verifies the transfer and checks if it is valid (spends existing funds + the net balance is 0).
......@@ -213,12 +216,12 @@ func (reality *Reality) verifyTransfer(inputs []*objectstorage.CachedObject, out
return errors.New("missing input in transfer")
}
transferOutput := cachedInput.Get().(*TransferOutput)
if !reality.DescendsFrom(transferOutput.GetRealityId()) {
input := cachedInput.Get().(*TransferOutput)
if !reality.DescendsFrom(input.GetRealityId()) {
return errors.New("the referenced funds do not exist in this reality")
}
for _, balance := range transferOutput.GetBalances() {
for _, balance := range input.GetBalances() {
totalColoredBalances[balance.GetColor()] += balance.GetValue()
}
}
......@@ -243,24 +246,24 @@ func (reality *Reality) verifyTransfer(inputs []*objectstorage.CachedObject, out
return nil
}
// Marks the consumed inputs as spent and returns the corresponding ConflictSets if the inputs have been consumed before.
func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash TransferHash, outputs map[AddressHash][]*ColoredBalance) (conflictSets objectstorage.CachedObjects, err error) {
conflictSets = make(objectstorage.CachedObjects, 0)
// Marks the consumed inputs as spent and returns the corresponding Conflict if the inputs have been consumed before.
func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transferHash TransferHash, outputs map[AddressHash][]*ColoredBalance) (conflicts objectstorage.CachedObjects, err error) {
conflicts = make(objectstorage.CachedObjects, 0)
for _, input := range inputs {
consumedTransferOutput := input.Get().(*TransferOutput)
consumedInput := input.Get().(*TransferOutput)
if consumersToElevate, consumeErr := consumedTransferOutput.addConsumer(transferHash, outputs); consumeErr != nil {
if consumersToElevate, consumeErr := consumedInput.addConsumer(transferHash, outputs); consumeErr != nil {
err = consumeErr
return
} else if consumersToElevate != nil {
if conflictSet, conflictErr := reality.retrieveConflictSetForConflictingInput(consumedTransferOutput, consumersToElevate); conflictErr != nil {
if conflict, conflictErr := reality.retrieveConflictForConflictingInput(consumedInput, consumersToElevate); conflictErr != nil {
err = conflictErr
return
} else {
conflictSets = append(conflictSets, conflictSet)
conflicts = append(conflicts, conflict)
}
}
......@@ -270,50 +273,61 @@ func (reality *Reality) consumeInputs(inputs objectstorage.CachedObjects, transf
return
}
func (reality *Reality) retrieveConflictSetForConflictingInput(input *TransferOutput, consumersToElevate map[TransferHash][]AddressHash) (conflictSet *objectstorage.CachedObject, err error) {
func (reality *Reality) retrieveConflictForConflictingInput(input *TransferOutput, consumersToElevate map[TransferHash][]AddressHash) (conflict *objectstorage.CachedObject, err error) {
conflictSetId := NewConflictSetId(input.GetTransferHash(), input.GetAddressHash())
if len(consumersToElevate) >= 1 {
newConflictSet := newConflictSet(conflictSetId)
newConflictSet.ledgerState = reality.ledgerState
newConflict := newConflictSet(conflictSetId)
newConflict.ledgerState = reality.ledgerState
conflictSet = reality.ledgerState.conflictSets.Store(newConflictSet)
conflict = reality.ledgerState.conflictSets.Store(newConflict)
err = reality.createRealityForConflictingConsumers(consumersToElevate, conflictSet.Get().(*Conflict))
if err != nil {
return
}
err = reality.createRealityForConsumerOfConflictingInput(consumersToElevate, conflict.Get().(*Conflict))
} else {
conflictSet, err = reality.ledgerState.conflictSets.Load(conflictSetId[:])
if err != nil {
return
if conflict, err = reality.ledgerState.conflictSets.Load(conflictSetId[:]); err == nil {
conflict.Get().(*Conflict).ledgerState = reality.ledgerState
}
conflictSet.Get().(*Conflict).ledgerState = reality.ledgerState
}
return
}
func (reality *Reality) createRealityForConflictingConsumers(conflictingConsumers map[TransferHash][]AddressHash, conflictSet *Conflict) (err error) {
for transferHash, addressHashes := range conflictingConsumers {
// determine RealityId
elevatedRealityId := transferHash.ToRealityId()
// create new reality for every Transfer
reality.CreateReality(elevatedRealityId).Consume(func(object objectstorage.StorableObject) {
elevatedReality := object.(*Reality)
// register Reality <-> Conflict
conflictSet.AddReality(elevatedRealityId)
elevatedReality.AddConflictSet(conflictSet.GetId())
// elevate TransferOutputs to the new Reality
for _, addressHash := range addressHashes {
if err = reality.elevateTransferOutput(NewTransferOutputReference(transferHash, addressHash), elevatedReality); err != nil {
return
// Creates a Reality for the consumers of the conflicting inputs and registers it as part of the corresponding Conflict.
func (reality *Reality) createRealityForConsumerOfConflictingInput(consumersOfConflictingInput map[TransferHash][]AddressHash, conflict *Conflict) (err error) {
for transferHash, addressHashes := range consumersOfConflictingInput {
var elevatedRealityId = transferHash.ToRealityId()
var realityIsNew bool
var cachedElevatedReality *objectstorage.CachedObject
// Retrieve the Reality for this Transfer or create one if no Reality exists, yet.
if cachedElevatedReality, err = reality.ledgerState.realities.ComputeIfAbsent(elevatedRealityId[:], func(key []byte) (object objectstorage.StorableObject, e error) {
newReality := newReality(elevatedRealityId, reality.id)
newReality.ledgerState = reality.ledgerState
realityIsNew = true
return newReality, nil
}); err == nil {
cachedElevatedReality.Store().Consume(func(object objectstorage.StorableObject) {
elevatedReality := object.(*Reality)
// We register every Conflict with the Reality (independent if it is "new" or not), to reflect its
// association to all corresponding Conflicts. (Note: A Reality can be part of multiple Conflicts if the
// Transfer that is associated to this Reality consumes multiple inputs.
conflict.AddReality(elevatedRealityId)
elevatedReality.AddConflict(conflict.GetId())
// A transaction can consume multiple inputs. We only elevate the consumers of a Reality once (when the
// Reality is created the first time).
if realityIsNew {
for _, addressHash := range addressHashes {
if err = reality.elevateTransferOutput(NewTransferOutputReference(transferHash, addressHash), elevatedReality); err != nil {
return
}
}
}
}
})
})
}
}
return
......
package ledgerstate
import (
"strings"
"github.com/iotaledger/goshimmer/packages/graphviz"
"github.com/emicklei/dot"
"github.com/iotaledger/hive.go/objectstorage"
)
type transferOutputId [transferHashLength + addressHashLength]byte
type Visualizer struct {
ledgerState *LedgerState
graph *dot.Graph
realitySubGraphs map[RealityId]*dot.Graph
transferOutputNodes map[transferOutputId]dot.Node
}
func NewVisualizer(ledgerState *LedgerState) *Visualizer {
return &Visualizer{
ledgerState: ledgerState,
}
}
func (visualizer *Visualizer) RenderTransferOutputs(pngFileName string) error {
visualizer.reset()
visualizer.graph.Attr("ranksep", "1.0 equally")
visualizer.graph.Attr("compound", "true")
visualizer.ledgerState.ForEachTransferOutput(func(object *objectstorage.CachedObject) bool {
object.Consume(func(object objectstorage.StorableObject) {
visualizer.drawTransferOutput(object.(*TransferOutput))
})
return true
}, MAIN_REALITY_ID)
return graphviz.RenderPNG(visualizer.graph, pngFileName)
}
func (visualizer *Visualizer) reset() *Visualizer {
visualizer.graph = dot.NewGraph(dot.Directed)
visualizer.realitySubGraphs = make(map[RealityId]*dot.Graph)
visualizer.transferOutputNodes = make(map[transferOutputId]dot.Node)
return visualizer
}
func (visualizer *Visualizer) drawTransferOutput(transferOutput *TransferOutput) dot.Node {
transferOutputIdentifier := visualizer.generateTransferOutputId(transferOutput)
transferOutputNode, transferOutputDrawn := visualizer.transferOutputNodes[transferOutputIdentifier]
if !transferOutputDrawn {
transferOutputNode = visualizer.getRealitySubGraph(transferOutput.GetRealityId()).Node("OUTPUT: " + strings.Trim(transferOutput.GetTransferHash().String(), "\x00") + " / " + strings.Trim(transferOutput.GetAddressHash().String(), "\x00"))
visualizer.styleTransferOutputNode(transferOutputNode)
for transferHash, addresses := range transferOutput.GetConsumers() {
for _, addressHash := range addresses {
visualizer.ledgerState.GetTransferOutput(NewTransferOutputReference(transferHash, addressHash)).Consume(func(object objectstorage.StorableObject) {
transferOutputNode.Edge(visualizer.drawTransferOutput(object.(*TransferOutput)))
})
}
}
visualizer.transferOutputNodes[transferOutputIdentifier] = transferOutputNode
}
return transferOutputNode
}
func (visualizer *Visualizer) generateTransferOutputId(transferOutput *TransferOutput) (result transferOutputId) {
transferHash := transferOutput.GetTransferHash()
addressHash := transferOutput.GetAddressHash()
copy(result[:], transferHash[:])
copy(result[transferHashLength:], addressHash[:])
return
}
func (Visualizer *Visualizer) styleTransferOutputNode(transferOutputNode dot.Node) {
transferOutputNode.Attr("fontname", "helvetica")
transferOutputNode.Attr("fontsize", "11")
transferOutputNode.Attr("style", "filled")
transferOutputNode.Attr("shape", "box")
transferOutputNode.Attr("color", "#6C8EBF")
transferOutputNode.Attr("fillcolor", "white")
}
func (visualizer *Visualizer) getRealitySubGraph(realityId RealityId) *dot.Graph {
realityGraph, exists := visualizer.realitySubGraphs[realityId]
if !exists {
visualizer.ledgerState.GetReality(realityId).Consume(func(object objectstorage.StorableObject) {
reality := object.(*Reality)
parentRealities := reality.GetParentRealityIds()
switch true {
case len(parentRealities) > 1:
realityGraph = visualizer.getRealitySubGraph(MAIN_REALITY_ID).Subgraph(visualizer.generateRealityName(parentRealities.ToList()...), dot.ClusterOption{})
visualizer.styleRealitySubGraph(realityGraph, realityTypeAggregated)
//dummyNode := realityGraph.Node(ledgerState.generateRealityName(parentRealities.ToList()...) + "_dummy")
//dummyNode.Attr("shape", "point")
//dummyNode.Attr("style", "invis")
//dummyNode.Attr("peripheries", "0")
//dummyNode.Attr("height", "0")
//dummyNode.Attr("width", "0")
case len(parentRealities) == 1:
for parentRealityId := range parentRealities {
realityGraph = visualizer.getRealitySubGraph(parentRealityId).Subgraph(visualizer.generateRealityName(realityId), dot.ClusterOption{})
visualizer.styleRealitySubGraph(realityGraph, realityTypeDefault)
}
default:
realityGraph = visualizer.graph.Subgraph(visualizer.generateRealityName(realityId), dot.ClusterOption{})
visualizer.styleRealitySubGraph(realityGraph, realityTypeMain)
}
})
visualizer.realitySubGraphs[realityId] = realityGraph
}
return realityGraph
}
func (visualizer *Visualizer) styleRealitySubGraph(realitySubGraph *dot.Graph, realityType realityType) {
realitySubGraph.Attr("fontname", "helvetica")
realitySubGraph.Attr("fontsize", "11")
realitySubGraph.Attr("style", "filled")
realitySubGraph.Attr("nodesep", "0")
switch realityType {
case realityTypeAggregated:
realitySubGraph.Attr("color", "#9673A6")
realitySubGraph.Attr("fillcolor", "#E1D5E7")
case realityTypeMain:
realitySubGraph.Attr("color", "#D6B656")
realitySubGraph.Attr("fillcolor", "#FFF2CC")
case realityTypeDefault:
realitySubGraph.Attr("color", "#6C8EBF")
realitySubGraph.Attr("fillcolor", "#DAE8FC")
}
}
func (visualizer *Visualizer) generateRealityName(realityIds ...RealityId) string {
if len(realityIds) > 1 {
result := "AGGREGATED REALITY [ "
realityIdCount := len(realityIds)
for id, realityId := range realityIds {
if id == realityIdCount-1 {
result += strings.Trim(realityId.String(), "\x00")
} else {
result += strings.Trim(realityId.String(), "\x00") + " + "
}
}
result += " ]"
return result
} else {
if realityIds[0] == MAIN_REALITY_ID {
return strings.Trim(realityIds[0].String(), "\x00")
}
return "REALITY [ " + strings.Trim(realityIds[0].String(), "\x00") + " ]"
}
}
type realityType int
const (
realityTypeAggregated = iota
realityTypeMain
realityTypeDefault
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment