From dc37f4bcfc17cc11ff55680016717d3af1c28448 Mon Sep 17 00:00:00 2001 From: "Filip.Gumula" Date: Tue, 14 Nov 2023 11:33:28 +0100 Subject: [PATCH] Added the oDot method to VerkleTrie for a human-readable string representation in Dot format. --- .../trie/verkle/SimpleVerkleTrie.java | 13 ++++++++++ .../besu/ethereum/trie/verkle/VerkleTrie.java | 7 ++++++ .../ethereum/trie/verkle/node/BranchNode.java | 25 +++++++++++++++++++ .../trie/verkle/node/InternalNode.java | 25 +++++++++++++++++++ .../ethereum/trie/verkle/node/LeafNode.java | 18 +++++++++++++ .../besu/ethereum/trie/verkle/node/Node.java | 8 ++++++ .../trie/verkle/node/NullLeafNode.java | 12 +++++++++ .../ethereum/trie/verkle/node/NullNode.java | 11 ++++++++ .../ethereum/trie/verkle/node/StemNode.java | 22 ++++++++++++++++ .../ethereum/trie/verkle/node/StoredNode.java | 11 ++++++++ .../trie/verkle/SimpleVerkleTrieTest.java | 25 +++++++++++++++++++ 11 files changed, 177 insertions(+) diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrie.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrie.java index fa6c83e..03be1f8 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrie.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrie.java @@ -141,4 +141,17 @@ public void commit(final NodeUpdater nodeUpdater) { root = root.accept(new HashVisitor(), Bytes.EMPTY); root = root.accept(new CommitVisitor(nodeUpdater), Bytes.EMPTY); } + + /** + * Returns the DOT representation of the entire Verkle Trie. + * + * @return The DOT representation of the Verkle Trie. + */ + + public String toDotTree() { + StringBuilder result = new StringBuilder("digraph VerkleTrie {\n"); + Node root = getRoot(); + result.append(root.toDot()); + return result.append("}\n").toString(); + } } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/VerkleTrie.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/VerkleTrie.java index 7652994..5f271af 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/VerkleTrie.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/VerkleTrie.java @@ -62,4 +62,11 @@ public interface VerkleTrie { * @param nodeUpdater used to store the node values */ void commit(NodeUpdater nodeUpdater); + + /** + * Returns the DOT representation of the entire Verkle Trie. + * + * @return The DOT representation of the Verkle Trie. + */ + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/BranchNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/BranchNode.java index 2d77fd2..b232483 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/BranchNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/BranchNode.java @@ -241,4 +241,29 @@ public String print() { } return builder.toString(); } + + /** + * Generates DOT representation for the BranchNode. + * + * @return DOT representation of the BranchNode. + */ + @Override + public String toDot() { + StringBuilder result = new StringBuilder() + .append(getClass().getSimpleName()).append(getLocation().orElse(Bytes.EMPTY)) + .append("\", location=\"").append(getLocation().orElse(Bytes.EMPTY)) + .append("\", commitment=\"").append(getHash().orElse(Bytes32.ZERO)).append("\"]\n"); + + for (Node child : getChildren()) { + String edgeString = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + " -> " + child.getClass().getSimpleName() + + child.getLocation().orElse(Bytes.EMPTY) + "\n"; + + if(!result.toString().contains(edgeString)) { + result.append(edgeString); + } + result.append(child.toDot()); + } + return result.toString(); + } + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/InternalNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/InternalNode.java index c9a3656..9702b25 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/InternalNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/InternalNode.java @@ -158,4 +158,29 @@ public String print() { } return builder.toString(); } + + /** + * Generates DOT representation for the InternalNode. + * + * @return DOT representation of the InternalNode. + */ + @Override + public String toDot() { + StringBuilder result = new StringBuilder() + .append(getClass().getSimpleName()).append(getLocation().orElse(Bytes.EMPTY)) + .append("[location=\"").append(getLocation().orElse(Bytes.EMPTY)) + .append("\", commitment=\"").append(getHash().orElse(Bytes32.ZERO)).append("\"]\n"); + + for (Node child : getChildren()) { + String edgeString = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + " -> " + child.getClass().getSimpleName() + + child.getLocation().orElse(Bytes.EMPTY) + "\n"; + + if(!result.toString().contains(edgeString)) { + result.append(edgeString); + } + result.append(child.toDot()); + } + return result.toString(); + } + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/LeafNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/LeafNode.java index e6a744d..d98b844 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/LeafNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/LeafNode.java @@ -159,4 +159,22 @@ public boolean isDirty() { public String print() { return "Leaf:" + getValue().map(Object::toString).orElse("empty"); } + + /** + * Generates DOT representation for the LeafNode. + * + * @return DOT representation of the LeafNode. + */ + @Override + public String toDot() { + Bytes locationBytes = getLocation().orElse(Bytes.EMPTY); + + return new StringBuilder() + .append(getClass().getSimpleName()).append(locationBytes) + .append("[location=\"").append(locationBytes) + .append("\", suffix=\"").append(locationBytes.get(locationBytes.size() - 1)) + .append("\", value=\"").append(getValue().orElse(null)).append("\"]\n") + .toString(); + } + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/Node.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/Node.java index b6abba6..bc841ec 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/Node.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/Node.java @@ -125,4 +125,12 @@ default List> getChildren() { * @return A string representation of the node. */ String print(); + + /** + * Generates DOT representation for the node. + * + * @return DOT representation of the node. + */ + String toDot(); + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullLeafNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullLeafNode.java index 11ce30d..36ee56d 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullLeafNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullLeafNode.java @@ -104,6 +104,17 @@ public String print() { return "[NULL-LEAF]"; } + /** + * Generates DOT representation for the NullLeafNode. + * + * @return DOT representation of the NullLeafNode. + */ + @Override + public String toDot() { + String result = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + " [location=\"" + getLocation().orElse(Bytes.EMPTY) + "\"]\n"; + return result; + } + /** * Check if the `NullNode` is marked as dirty (needing to be persisted). * @@ -123,4 +134,5 @@ public boolean isDirty() { public void markDirty() { // do nothing } + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullNode.java index 4ca0943..69e3d47 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/NullNode.java @@ -104,6 +104,17 @@ public String print() { return "[NULL]"; } + /** + * Generates DOT representation for the NullNode. + * + * @return DOT representation of the NullNode. + */ + @Override + public String toDot() { + String result = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + "[location=\"" + getLocation().orElse(Bytes.EMPTY) + "\"]\n"; + return result; + } + /** * Check if the `NullNode` is marked as dirty (needing to be persisted). * diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StemNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StemNode.java index 2a16fbd..17b71b9 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StemNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StemNode.java @@ -297,4 +297,26 @@ public String print() { private Bytes extractStem(final Bytes stemValue) { return stemValue.slice(0, 31); } + + @Override + public String toDot() { + StringBuilder result = new StringBuilder() + .append(getClass().getSimpleName()).append(getLocation().orElse(Bytes.EMPTY)) + .append("[location=\"").append(getLocation().orElse(Bytes.EMPTY)) + .append("\", stem=\"").append(getStem()) + .append("\", leftCommitment=\"").append(getLeftCommitment().orElse(Bytes32.ZERO)) + .append("\", rightCommitment=\"").append(getRightCommitment().orElse(Bytes32.ZERO)).append("\"]\n"); + + for (Node child : getChildren()) { + String edgeString = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + " -> " + child.getClass().getSimpleName() + + child.getLocation().orElse(Bytes.EMPTY) + "\n"; + + if(!result.toString().contains(edgeString)) { + result.append(edgeString); + } + result.append(child.toDot()); + } + return result.toString(); + } + } diff --git a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StoredNode.java b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StoredNode.java index 53bd6a9..16e0b61 100644 --- a/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StoredNode.java +++ b/src/main/java/org/hyperledger/besu/ethereum/trie/verkle/node/StoredNode.java @@ -168,6 +168,17 @@ public String print() { return String.format("(stored) %s", node.print()); } + /** + * Generates DOT representation for the StoredNode. + * + * @return DOT representation of the StoredNode. + */ + @Override + public String toDot() { + String result = getClass().getSimpleName() + getLocation().orElse(Bytes.EMPTY) + "[location=\"" + getLocation().orElse(Bytes.EMPTY) + "\"]\n"; + return result; + } + private Node load() { if (loadedNode.isEmpty()) { loadedNode = nodeFactory.retrieve(location, null); diff --git a/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrieTest.java b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrieTest.java index 430b0bd..68bb1ea 100644 --- a/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrieTest.java +++ b/src/test/java/org/hyperledger/besu/ethereum/trie/verkle/SimpleVerkleTrieTest.java @@ -25,6 +25,31 @@ public class SimpleVerkleTrieTest { + + @Test + public void toDotTreeWithOneValue() { + SimpleVerkleTrie trie = new SimpleVerkleTrie<>(); + Bytes32 key = Bytes32.fromHexString("0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"); + Bytes32 value = Bytes32.fromHexString("0x1000000000000000000000000000000000000000000000000000000000000000"); + trie.put(key, value); + + System.out.println(trie.toDotTree()); + } + + + @Test + public void toDotTreeWithTwoValues() { + SimpleVerkleTrie trie = new SimpleVerkleTrie(); + Bytes32 key1 = Bytes32.fromHexString("0x00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"); + Bytes32 value1 = Bytes32.fromHexString("0x1000000000000000000000000000000000000000000000000000000000000000"); + Bytes32 key2 = Bytes32.fromHexString("0x00112233445566778899aabbccddeeff00112233445566778899aabbccddee00"); + Bytes32 value2 = Bytes32.fromHexString("0x0100000000000000000000000000000000000000000000000000000000000000"); + + trie.put(key1, value1); + trie.put(key2, value2); + + System.out.println(trie.toDotTree()); + } @Test public void testEmptyTrie() { SimpleVerkleTrie trie = new SimpleVerkleTrie();