Skip to content

Commit

Permalink
Simplified the tables printed by the commands
Browse files Browse the repository at this point in the history
  • Loading branch information
spoto committed Mar 4, 2024
1 parent aa5b3ce commit 14aba2f
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
import java.util.logging.Logger;

import io.hotmoka.crypto.Hex;
import io.mokamint.node.api.ChainPortion;
import io.mokamint.node.api.ConsensusConfig;
import io.mokamint.node.api.DatabaseException;
import io.mokamint.node.api.GenesisBlockDescription;
import io.mokamint.node.api.NodeException;
Expand Down Expand Up @@ -98,25 +96,49 @@ public String toString(int pos, Table table) {
}

private class MyTable extends AbstractTable {
private MyTable(byte[][] hashes, ConsensusConfig<?, ?> config, LocalDateTime startDateTimeUTC, RemotePublicNode remote) throws NoSuchAlgorithmException, DatabaseException, TimeoutException, InterruptedException, NodeException {
super(new Row("", "block hash (" + config.getHashingForBlocks() + ")",
private final RemotePublicNode remote;
private final byte[][] hashes;
private final LocalDateTime startDateTimeUTC;

private MyTable(byte[] genesisHash, RemotePublicNode remote) throws NoSuchAlgorithmException, DatabaseException, TimeoutException, InterruptedException, NodeException {
super(mkHeader(remote), 5, json());

this.remote = remote;

LOGGER.info("requesting hashes in the height interval [" + from + ", " + (from + count) + ")");
this.hashes = remote.getChainPortion(from, count).getHashes().toArray(byte[][]::new);
this.startDateTimeUTC = getStartDateTimeUTC(genesisHash);

for (int pos = hashes.length - 1; pos >= 0; pos--)
add(pos);
}

private static Row mkHeader(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException {
var config = remote.getConfig();
return new Row("", "block hash (" + config.getHashingForBlocks() + ")",
"created (UTC)", "node's public key (" + config.getSignatureForBlocks() + ", base58)",
"miner's public key (" + config.getSignatureForDeadlines() + ", base58)"), 5, json());

int pos = 1;
for (byte[] hash: hashes)
add(hashes, hash, startDateTimeUTC, remote, pos++);
"miner's public key (" + config.getSignatureForDeadlines() + ", base58)");
}

private void add(byte[][] hashes, byte[] hash, LocalDateTime startDateTimeUTC, RemotePublicNode remote, int pos) throws NoSuchAlgorithmException, DatabaseException, TimeoutException, InterruptedException, NodeException {
long height = from + hashes.length - pos;

private LocalDateTime getStartDateTimeUTC(byte[] genesisHash) throws DatabaseException, NoSuchAlgorithmException, TimeoutException, InterruptedException, NodeException {
var maybeGenesis = remote.getBlockDescription(genesisHash);
if (maybeGenesis.isEmpty())
throw new DatabaseException("The node has a genesis hash but it is bound to no block!");
else if (maybeGenesis.get() instanceof GenesisBlockDescription gbd)
return gbd.getStartDateTimeUTC();
else
throw new DatabaseException("The type of the genesis block is inconsistent!");
}

private void add(int pos) throws NoSuchAlgorithmException, DatabaseException, TimeoutException, InterruptedException, NodeException {
long height = from + pos;
String rowHeight = height + ":";
String rowHash = Hex.toHexString(hashes[hashes.length - pos]);
String rowHash = Hex.toHexString(hashes[pos]);
String rowCreated;
String rowNodePublicKey;
String rowMinerPublicKey;

var maybeBlockDescription = remote.getBlockDescription(hashes[hashes.length - pos]);
var maybeBlockDescription = remote.getBlockDescription(hashes[pos]);
if (maybeBlockDescription.isEmpty()) {
if (height == 0L) {
rowCreated = startDateTimeUTC.format(FORMATTER);
Expand Down Expand Up @@ -156,23 +178,9 @@ private void body(RemotePublicNode remote) throws CommandException, DatabaseExce
if (from == -1L)
from = Math.max(0L, info.getLength() - 1 - count + 1);

LOGGER.info("requesting hashes in the height interval [" + from + ", " + (from + count) + ")");
ChainPortion chain = remote.getChainPortion(from, count);

if (chain.getHashes().count() != 0) {
var maybeGenesisHash = info.getGenesisHash();
if (maybeGenesisHash.isPresent()) {
var maybeGenesis = remote.getBlockDescription(maybeGenesisHash.get());
if (maybeGenesis.isEmpty())
throw new DatabaseException("The node has a genesis hash but it is bound to no block!");
else if (maybeGenesis.get() instanceof GenesisBlockDescription gbd)
new MyTable(chain.getHashes().toArray(byte[][]::new), remote.getConfig(), gbd.getStartDateTimeUTC(), remote).print();
else
throw new DatabaseException("The type of the genesis block is inconsistent!");
}
else
throw new DatabaseException("The node has no genesis hash it contains some blocks!");
}
var maybeGenesisHash = info.getGenesisHash();
if (maybeGenesisHash.isPresent())
new MyTable(maybeGenesisHash.get(), remote).print();
}
catch (NoSuchAlgorithmException e) {
throw new CommandException("Unknown hashing algorithm in the head of the chain of the node at \"" + publicUri() + "\"!", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@

import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import io.hotmoka.crypto.Hex;
import io.mokamint.node.MempoolPortions;
import io.mokamint.node.api.ConsensusConfig;
import io.mokamint.node.api.MempoolEntry;
import io.mokamint.node.api.MempoolPortion;
import io.mokamint.node.api.NodeException;
import io.mokamint.node.remote.api.RemotePublicNode;
import io.mokamint.node.tools.internal.AbstractPublicRpcCommand;
import io.mokamint.tools.AbstractRow;
import io.mokamint.tools.AbstractTable;
import io.mokamint.tools.CommandException;
import jakarta.websocket.EncodeException;
import io.mokamint.tools.Table;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help.Ansi;
import picocli.CommandLine.Option;
Expand All @@ -47,7 +44,7 @@ public class List extends AbstractPublicRpcCommand {

private final static Logger LOGGER = Logger.getLogger(List.class.getName());

private void body(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException, CommandException {
private void body(RemotePublicNode remote) throws CommandException, TimeoutException, InterruptedException, NodeException{
if (count < 0)
throw new CommandException("count cannot be negative!");

Expand All @@ -57,76 +54,65 @@ private void body(RemotePublicNode remote) throws TimeoutException, InterruptedE
if (from == -1L)
from = (int) Math.max(0, remote.getMempoolInfo().getSize() - count);

LOGGER.info("requesting mempool entries with index in the interval [" + from + ", " + (from + count) + ")");
var mempool = remote.getMempoolPortion(from, count);
new MyTable(remote).print();
}

if (json()) {
try {
System.out.println(new MempoolPortions.Encoder().encode(mempool));
}
catch (EncodeException e) {
throw new CommandException("Cannot encode a mempool portion of the node at \"" + publicUri() + "\" in JSON format!", e);
private static class Row extends AbstractRow {
private final String height;
private final String hash;
private final String priority;

private Row(String height, String hash, String priority) {
this.height = height;
this.hash = hash;
this.priority = priority;
}

@Override
public String getColumn(int index) {
switch (index) {
case 0: return height;
case 1: return hash;
case 2: return priority;
default: throw new IndexOutOfBoundsException(index);
}
}
else if (mempool.getEntries().count() != 0)
new ListMempool(mempool, remote);

@Override
public String toString(int pos, Table table) {
String result = String.format("%s %s %s",
rightAlign(height, table.getSlotsForColumn(0)),
center(hash, table.getSlotsForColumn(1)),
rightAlign(priority, table.getSlotsForColumn(2)));

return pos == 0 ? Ansi.AUTO.string("@|green " + result + "|@") : result;
}
}

private class ListMempool {
private final ConsensusConfig<?, ?> config;
private class MyTable extends AbstractTable {
private final MempoolEntry[] entries;
private final String[] heights;
private final int slotsForHeight;
private final String[] hashes;
private final int slotsForHash;
private final String[] priorities;
private final int slotsForPriority;

/**
* Lists the entries in {@code mempool}.
*
* @param mempool the mempool portion to list
* @param the remote node
* @throws TimeoutException if some connection timed-out
* @throws InterruptedException if some connection was interrupted while waiting
* @throws NodeException if the remote node could not complete the operation
*/
private ListMempool(MempoolPortion mempool, RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException {
this.config = remote.getConfig();
this.entries = mempool.getEntries().toArray(MempoolEntry[]::new);
this.heights = new String[1 + entries.length];
this.hashes = new String[heights.length];
this.priorities = new String[heights.length];
fillColumns();
this.slotsForHeight = Stream.of(heights).mapToInt(String::length).max().getAsInt();
this.slotsForHash = Stream.of(hashes).mapToInt(String::length).max().getAsInt();
this.slotsForPriority = Stream.of(priorities).mapToInt(String::length).max().getAsInt();
printRows();
}

private void fillColumns() {
heights[0] = "";
hashes[0] = "tx hash (" + config.getHashingForTransactions() + ")";
priorities[0] = "priority";

for (int pos = 1; pos < hashes.length; pos++) {
heights[pos] = (from + entries.length - pos) + ":";
hashes[pos] = Hex.toHexString(entries[entries.length - pos].getHash());
priorities[pos] = String.valueOf(entries[entries.length - pos].getPriority());
}
private MyTable(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException {
super(mkHeader(remote), 3, json());

LOGGER.info("requesting mempool entries with index in the interval [" + from + ", " + (from + count) + ")");
this.entries = remote.getMempoolPortion(from, count).getEntries().toArray(MempoolEntry[]::new);

for (int pos = entries.length - 1; pos >= 0; pos--)
add(pos);
}

private void printRows() {
IntStream.iterate(0, i -> i + 1).limit(heights.length).mapToObj(this::format).forEach(System.out::println);
private static Row mkHeader(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException {
var config = remote.getConfig();
return new Row("", "tx hash (" + config.getHashingForTransactions() + ")", "priority");
}

private String format(int pos) {
String result = String.format("%s %s %s",
rightAlign(heights[pos], slotsForHeight),
center(hashes[pos], slotsForHash),
rightAlign(priorities[pos], slotsForPriority));
private void add(int pos) {
String height = (from + pos) + ":";
String hash = Hex.toHexString(entries[pos].getHash());
String priority = String.valueOf(entries[pos].getPriority());

return pos == 0 ? Ansi.AUTO.string("@|green " + result + "|@") : result;
add(new Row(height, hash, priority));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,77 +16,56 @@

package io.mokamint.node.tools.internal.tasks;

import static io.hotmoka.exceptions.CheckSupplier.check;
import static io.hotmoka.exceptions.UncheckFunction.uncheck;

import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import io.mokamint.node.TaskInfos;
import io.mokamint.node.api.NodeException;
import io.mokamint.node.api.TaskInfo;
import io.mokamint.node.remote.api.RemotePublicNode;
import io.mokamint.node.tools.internal.AbstractPublicRpcCommand;
import io.mokamint.tools.AbstractRow;
import io.mokamint.tools.AbstractTable;
import io.mokamint.tools.CommandException;
import jakarta.websocket.EncodeException;
import io.mokamint.tools.Table;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help.Ansi;

@Command(name = "ls", description = "List the tasks of a node.")
public class List extends AbstractPublicRpcCommand {

private void body(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException, CommandException {
var infos = remote.getTaskInfos().sorted().toArray(TaskInfo[]::new);

if (json()) {
try {
var encoder = new TaskInfos.Encoder();
System.out.println(check(EncodeException.class, () ->
Stream.of(infos).map(uncheck(encoder::encode)).collect(Collectors.joining(",", "[", "]"))
));
}
catch (EncodeException e) {
throw new CommandException("Cannot encode the tasks of the node at " + publicUri() + " in JSON format!", e);
}
}
else if (infos.length > 0)
new ListTasks(infos);
new MyTable(remote).print();
}

private class ListTasks {
private final String[] descriptions;
private final int slotsForDescription;

/**
* Lists the tasks with the given {@code infos}.
*
* @param infos the tasks information
*/
private ListTasks(TaskInfo[] infos) {
this.descriptions = new String[1 + infos.length];
fillColumns(infos);
this.slotsForDescription = Stream.of(descriptions).mapToInt(String::length).max().getAsInt();
printRows();
private static class Row extends AbstractRow {
private final String description;

private Row(String description) {
this.description = description;
}

private void fillColumns(TaskInfo[] infos) {
descriptions[0] = "description";

for (int pos = 1; pos < descriptions.length; pos++)
descriptions[pos] = infos[pos - 1].getDescription();
}

private void printRows() {
IntStream.iterate(0, i -> i + 1).limit(descriptions.length).mapToObj(this::format).forEach(System.out::println);

@Override
public String getColumn(int index) {
if (index == 0)
return description;
else
throw new IndexOutOfBoundsException(index);
}

private String format(int pos) {

@Override
public String toString(int pos, Table table) {
if (pos == 0)
return Ansi.AUTO.string("@|green " + String.format("%s", center(descriptions[pos], slotsForDescription)) + "|@");
return Ansi.AUTO.string("@|green " + String.format("%s", center(description, table.getSlotsForColumn(0))) + "|@");
else
return String.format("%s", leftAlign(descriptions[pos], slotsForDescription));
return String.format("%s", leftAlign(description, table.getSlotsForColumn(0)));
}
}

private class MyTable extends AbstractTable {

private MyTable(RemotePublicNode remote) throws TimeoutException, InterruptedException, NodeException {
super(new Row("description"), 1, json());

remote.getTaskInfos().sorted().map(TaskInfo::getDescription).map(Row::new).forEach(this::add);
}
}

Expand Down
4 changes: 2 additions & 2 deletions io-mokamint-node-tools/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@

// needed to inject CLI options or JSON serialization
opens io.mokamint.node.tools.internal to info.picocli;
opens io.mokamint.node.tools.internal.mempool to info.picocli;
opens io.mokamint.node.tools.internal.mempool to info.picocli, com.google.gson;
opens io.mokamint.node.tools.internal.miners to info.picocli;
opens io.mokamint.node.tools.internal.peers to info.picocli;
opens io.mokamint.node.tools.internal.config to info.picocli;
opens io.mokamint.node.tools.internal.chain to info.picocli, com.google.gson;
opens io.mokamint.node.tools.internal.tasks to info.picocli;
opens io.mokamint.node.tools.internal.tasks to info.picocli, com.google.gson;
opens io.mokamint.node.tools.internal.keys to info.picocli, com.google.gson;
opens io.mokamint.node.tools.internal.transactions to info.picocli, com.google.gson;

Expand Down

0 comments on commit 14aba2f

Please sign in to comment.