Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement File Storing Mechanism for toDot Method Output in VerkleTrie #11

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
020d368
Added the oDot method to VerkleTrie for a human-readable string repr…
Uacias Nov 14, 2023
c3cf923
Remove unnecessary description
Uacias Nov 14, 2023
715ef1a
Modification of toDot method, added showRepeatingEdges flag, to manag…
Uacias Nov 14, 2023
be43be6
Add toDot() method overload in Node interface and toDotTree() method …
Uacias Nov 14, 2023
63cbd82
Added the oDot method to VerkleTrie for a human-readable string repr…
Uacias Nov 14, 2023
cdcb410
Modification of toDot method, added showRepeatingEdges flag, to manag…
Uacias Nov 14, 2023
75a6377
Refactor toDot() method in nodes for improved readabiility by replaci…
Uacias Nov 15, 2023
975a050
Move tests related to toDot() method to a separate class 'DotDisplayT…
Uacias Nov 15, 2023
b8b1972
Modify toDotTrie() methods to use String.format()
Uacias Nov 15, 2023
e63d390
Small fix for toDotTree() methods
Uacias Nov 15, 2023
526beab
Add missing assert for the tests.
Uacias Nov 15, 2023
458c6b5
Move expected dot representations to separate files in resources. Mod…
Uacias Nov 15, 2023
ad4eecf
Initial solution setup - to be done
Uacias Nov 14, 2023
a8b721f
Enhancment of exportToDotFile() method in DotExporter
Uacias Nov 15, 2023
53413f3
Working solution, refactoring to be done
Uacias Nov 15, 2023
ac3c2b6
Refactor DotExporter, add dotTreeToFile() methods for SimpleVerkleTri…
Uacias Nov 16, 2023
f012882
Added SLF4J Logger to DotExporter class and dependencies
Uacias Nov 16, 2023
7d57383
Exception handling in DotExporter and use StringBuilder for efficienc…
Uacias Nov 16, 2023
a53d67a
Upgrade slf4j and logback to latest versions, export slff4j and logba…
Uacias Nov 17, 2023
0ebe839
spotlessApply
neotheprogramist Nov 18, 2023
a69c55d
fix tests & remove semicolon
neotheprogramist Nov 18, 2023
a6564c9
add working tests
neotheprogramist Nov 18, 2023
de98029
Regex for removing unnecessary newlines, adjust tests.
Uacias Nov 18, 2023
bbe9931
format - spotlessApply
Uacias Nov 18, 2023
deee991
Fix newline issue, tests pass, format
Uacias Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ dependencies {
implementation 'io.tmio:tuweni-rlp'
implementation 'org.hyperledger.besu:ipa-multipoint'
implementation 'org.hyperledger.besu.internal:trie:23.1.3-SNAPSHOT'
implementation 'org.slf4j:slf4j-api'
implementation 'ch.qos.logback:logback-classic'

testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
Expand Down
4 changes: 4 additions & 0 deletions gradle/versions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ dependencyManagement {

dependency 'org.assertj:assertj-core:3.24.2'

dependency 'org.slf4j:slf4j-api:2.0.9'

dependency 'ch.qos.logback:logback-classic:1.4.11'

dependencySet(group: 'io.tmio', version: '2.4.2') {
entry 'tuweni-bytes'
entry 'tuweni-rlp'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.common.base.Preconditions.checkNotNull;

import org.hyperledger.besu.ethereum.trie.NodeUpdater;
import org.hyperledger.besu.ethereum.trie.verkle.exporter.DotExporter;
import org.hyperledger.besu.ethereum.trie.verkle.node.InternalNode;
import org.hyperledger.besu.ethereum.trie.verkle.node.Node;
import org.hyperledger.besu.ethereum.trie.verkle.visitor.CommitVisitor;
Expand All @@ -26,6 +27,7 @@
import org.hyperledger.besu.ethereum.trie.verkle.visitor.PutVisitor;
import org.hyperledger.besu.ethereum.trie.verkle.visitor.RemoveVisitor;

import java.io.IOException;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;
Expand Down Expand Up @@ -141,4 +143,50 @@ public void commit(final NodeUpdater nodeUpdater) {
root = root.accept(new HashVisitor<V>(), Bytes.EMPTY);
root = root.accept(new CommitVisitor<V>(nodeUpdater), Bytes.EMPTY);
}

/**
* Returns the DOT representation of the entire Verkle Trie.
*
* @param showRepeatingEdges if true displays repeating edges; if false does not.
* @return The DOT representation of the Verkle Trie.
*/
public String toDotTree(Boolean showRepeatingEdges) {
return String.format(
"digraph VerkleTrie {\n%s\n}",
getRoot().toDot(showRepeatingEdges).replaceAll("^\\n+|\\n+$", ""));
}

/**
* Returns the DOT representation of the entire Verkle Trie.
*
* <p>The representation does not contain repeating edges.
*
* @return The DOT representation of the Verkle Trie.
*/
public String toDotTree() {
StringBuilder result = new StringBuilder("digraph VerkleTrie {\n");
Node<V> root = getRoot();
result.append(root.toDot());
return result.append("}").toString();
}

/**
* Exports the Verkle Trie DOT representation to a '.gv' file located in the current directory.
* The default file name is "VerkleTree.gv".
*
* @throws IOException if an I/O error occurs.
*/
public void dotTreeToFile() throws IOException {
DotExporter.exportToDotFile(toDotTree());
}

/**
* /** Exports the Verkle Trie DOT representation to a '.gv' file located at the specified path.
*
* @param path The location where the DOT file will be saved.
* @throws IOException if ann I/O error occurs.
*/
public void dotTreeToFile(String path) throws IOException {
DotExporter.exportToDotFile(toDotTree(), path);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright Besu Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.trie.verkle.exporter;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Utility class for exporting Verkle Trie representations to DOT files. */
public class DotExporter {

private static final Logger LOG = LoggerFactory.getLogger(DotExporter.class);
private static final Pattern FILE_EXTENSION_PATTERN = Pattern.compile("\\.(dot|gv)$");
private static final String DEFAULT_FILE_NAME = "./VerkleTrie.gv";

/**
* Exports the Verkle Trie DOT representation to a '.gv' file located in the current directory.
* The default file name is "VerkleTrie.gv".
*
* @param verkleTrieDotString The DOT representation of the Verkle Trie.
* @throws IOException If an I/O error occurs during the export process.
*/
public static void exportToDotFile(String verkleTrieDotString) throws IOException {
exportToDotFile(verkleTrieDotString, DEFAULT_FILE_NAME);
}

/**
* Exports the Verkle Trie DOT representation to a '.gv' file located at the specified path.
*
* @param verkleTrieDotString The DOT representation of the Verkle Trie.
* @param filePath The location where the DOT file will be saved.
* @throws IOException If an I/O error occurs during the export process.
*/
public static void exportToDotFile(String verkleTrieDotString, String filePath)
throws IOException {
try {
if (filePath == null || filePath.isEmpty()) {
filePath = DEFAULT_FILE_NAME;
} else {
Matcher matcher = FILE_EXTENSION_PATTERN.matcher(filePath);
if (!matcher.find()) {
throw new IllegalArgumentException("Invalid file extension. Use .dot or .gv extension.");
}
}

Path path = Paths.get(filePath);

Files.createDirectories(path.getParent());

try (BufferedWriter writer =
new BufferedWriter(new FileWriter(path.toString(), StandardCharsets.UTF_8))) {
writer.write(verkleTrieDotString);
}

} catch (AccessDeniedException e) {
LOG.error(
"Access denied. Check write permissions for the file. Details: {}", e.getMessage(), e);
throw e;
} catch (FileSystemException e) {
LOG.error(
"File system issue. Check disk space and file system restrictions. Details: {}",
e.getMessage(),
e);
throw e;
} catch (IOException e) {
LOG.error("Error writing DOT file: {}. Details: {}", e.getMessage(), e);
throw e;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,39 @@ public String print() {
}
return builder.toString();
}

/**
* Generates DOT representation for the BranchNode.
*
* @return DOT representation of the BranchNode.
*/
@Override
public String toDot(Boolean showRepeatingEdges) {
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<V> child : getChildren()) {
String edgeString =
getClass().getSimpleName()
+ getLocation().orElse(Bytes.EMPTY)
+ " -> "
+ child.getClass().getSimpleName()
+ child.getLocation().orElse(Bytes.EMPTY)
+ "\n";

if (showRepeatingEdges || !result.toString().contains(edgeString)) {
result.append(edgeString);
}
result.append(child.toDot(showRepeatingEdges));
}

return result.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,39 @@ public String print() {
}
return builder.toString();
}

/**
* Generates DOT representation for the InternalNode.
*
* @return DOT representation of the InternalNode.
*/
@Override
public String toDot(Boolean showRepeatingEdges) {
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<V> child : getChildren()) {
String edgeString =
getClass().getSimpleName()
+ getLocation().orElse(Bytes.EMPTY)
+ " -> "
+ child.getClass().getSimpleName()
+ child.getLocation().orElse(Bytes.EMPTY)
+ "\n";

if (showRepeatingEdges || !result.toString().contains(edgeString)) {
result.append(edgeString);
}
result.append(child.toDot(showRepeatingEdges));
}

return result.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public LeafNode(final Bytes location, final V value) {
this.value = value;
this.valueSerializer = val -> (Bytes) val;
}

/**
* Constructs a new LeafNode with optional location, value.
*
Expand Down Expand Up @@ -104,6 +105,7 @@ public Optional<V> getValue() {
public Optional<Bytes> getLocation() {
return location;
}

/**
* Get the children of the node. A leaf node does not have children, so this method throws an
* UnsupportedOperationException.
Expand Down Expand Up @@ -159,4 +161,26 @@ 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(Boolean showRepeatingEdges) {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,23 @@ default List<Node<V>> getChildren() {
* @return A string representation of the node.
*/
String print();

/**
* Generates DOT representation for the Node.
*
* @param showRepeatingEdges If true, prints all edges; if false, prints only unique edges.
* @return DOT representation of the Node.
*/
String toDot(Boolean showRepeatingEdges);

/**
* Generates DOT representation for the Node.
*
* <p>Representation does not contain repeating edges.
*
* @return DOT representation of the Node.
*/
default String toDot() {
return toDot(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ public String print() {
return "[NULL-LEAF]";
}

/**
* Generates DOT representation for the NullLeafNode.
*
* @return DOT representation of the NullLeafNode.
*/
@Override
public String toDot(Boolean showRepeatingEdges) {
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).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ public String print() {
return "[NULL]";
}

/**
* Generates DOT representation for the NullNode.
*
* @return DOT representation of the NullNode.
*/
@Override
public String toDot(Boolean showRepeatingEdges) {
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).
*
Expand Down
Loading
Loading