From a8cf6ca4c8aaa6fe25d720ab31c493a11d99b602 Mon Sep 17 00:00:00 2001 From: Julian Konchunas Date: Thu, 25 Oct 2018 15:41:24 +0300 Subject: [PATCH 1/2] Add dag visualizer conflict colors --- chain/conflict_watcher.py | 3 ++ visualization/dag_visualizer.py | 58 ++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/chain/conflict_watcher.py b/chain/conflict_watcher.py index 3e61c50..1bb1b2c 100644 --- a/chain/conflict_watcher.py +++ b/chain/conflict_watcher.py @@ -18,6 +18,9 @@ def on_new_block_by_validator(self, block_hash, epoch_number, public_key): # public_key : [block_hash] def get_conflicts_by_block(self, block_hash): + if block_hash == self.dag.genesis_hash(): + return None + assert block_hash in self.blocks, "No block in conflict watcher with hash %r" % block_hash.hex() pubkey, epoch_number = self.blocks[block_hash] return self.get_conflicts_by_pubkey(pubkey, epoch_number) diff --git a/visualization/dag_visualizer.py b/visualization/dag_visualizer.py index 2f6bfc9..427389e 100644 --- a/visualization/dag_visualizer.py +++ b/visualization/dag_visualizer.py @@ -1,4 +1,5 @@ from graphviz import Digraph +from chain.conflict_watcher import ConflictWatcher # Usage # Just add two following lines where you want to visualize @@ -11,17 +12,24 @@ # DagVisualizer.visualize(dag, True) class DagVisualizer: - @staticmethod - def visualize(dag, view_immediately=False): + def __init__(self, dag, conflict_watcher=None): + self.dag = dag + self.watcher = conflict_watcher + self.reset_colors() + + def show(self): + self.render(name="dag", view_immediately=True) + + def render(self, name="dag", view_immediately=False): dot = Digraph(name='DAG', node_attr={ 'shape':'box',\ 'style': "rounded"}) dot.attr(rankdir = 'RL') links = [] - max_block_number = max(dag.blocks_by_number.keys()) + max_block_number = max(self.dag.blocks_by_number.keys()) for number in range(max_block_number+1): - block_list_by_number = dag.blocks_by_number.get(number, []) + block_list_by_number = self.dag.blocks_by_number.get(number, []) with dot.subgraph() as sub: sub.attr(rank = 'same') @@ -31,20 +39,48 @@ def visualize(dag, view_immediately=False): if number != 0: dot.edge(str(number), str(number-1), style="invis") + blocks_to_color = {} + #add blocks on this level if any for block in block_list_by_number: links += block.block.prev_hashes - blockhash = block.get_hash() - color = 'black' - if blockhash == dag.genesis_block().get_hash(): - color='blue' - sub.node(blockhash.hex()[0:6], color=color) + block_hash = block.get_hash() + color = self.get_block_color(block_hash) + sub.node(block_hash.hex()[0:6], color=color) - for _, signed_block in dag.blocks_by_hash.items(): + for _, signed_block in self.dag.blocks_by_hash.items(): block_hash = signed_block.get_hash() for prev_hash in signed_block.block.prev_hashes: dot.edge(block_hash.hex()[0:6], prev_hash.hex()[0:6], constraint='true') + + self.reset_colors() #set view to True to instantly render and open pdf #Note, that you will need 'graphviz' package installed dot.format = "png" - dot.render('visualization/dag.dot', view=view_immediately) + dot.render('visualization/' + name + '.dot', view=view_immediately) + + def reset_colors(self): + self.possible_conflict_colors = ["red", "orangered", "firebrick", "orange", "brown"] + self.blocks_color = {} + + # to show conflicting blocks in the same color + def get_block_color(self, block_hash): + if not self.watcher: + return "black" + + if block_hash in self.blocks_color: + return self.blocks_color[block_hash] + + conflicts = self.watcher.get_conflicts_by_block(block_hash) + if conflicts: + chosen_color = self.possible_conflict_colors.pop() + for conflict in conflicts: + self.blocks_color[conflict] = chosen_color + return chosen_color + + return "black" + + @staticmethod + def visualize(dag, view_immediately=False): + visualizer = DagVisualizer(dag) + visualizer.render("dag", view_immediately) From 18efa5af1c8edd641b9bd8856141f7080c2b39c5 Mon Sep 17 00:00:00 2001 From: Julian Konchunas Date: Thu, 25 Oct 2018 15:46:34 +0300 Subject: [PATCH 2/2] Add help for new Dag visualizer usage --- visualization/dag_visualizer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/visualization/dag_visualizer.py b/visualization/dag_visualizer.py index 427389e..8ee9d57 100644 --- a/visualization/dag_visualizer.py +++ b/visualization/dag_visualizer.py @@ -11,6 +11,10 @@ # from visualization.dag_visualizer import DagVisualizer # DagVisualizer.visualize(dag, True) +# To highlight conflicts you can do the following: +# visualizer = DagVisualizer(dag, conflict_watcher) +# visualizer.show() + class DagVisualizer: def __init__(self, dag, conflict_watcher=None): self.dag = dag