mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Shut down connection after response received
Various improvements...
This commit is contained in:
parent
1b83083ade
commit
7768e58b98
@ -81,6 +81,9 @@ public class GetInventoryRequester implements MessageListener, ConnectionListene
|
||||
GetInventoryResponse getInventoryResponse = (GetInventoryResponse) networkEnvelope;
|
||||
resultHandler.accept(getInventoryResponse.getInventory());
|
||||
shutDown();
|
||||
|
||||
// We shut down our connection after work as our node is not helpful for the network.
|
||||
connection.shutDown(CloseConnectionReason.CLOSE_REQUESTED_BY_PEER);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -29,8 +29,6 @@ import bisq.network.p2p.network.NetworkNode;
|
||||
import bisq.network.p2p.network.SetupListener;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.Capabilities;
|
||||
import bisq.common.app.Capability;
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
import bisq.common.file.JsonFileManager;
|
||||
import bisq.common.util.Utilities;
|
||||
@ -40,7 +38,6 @@ import java.time.Clock;
|
||||
import java.io.File;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -49,15 +46,18 @@ import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
@Slf4j
|
||||
public class InventoryMonitor {
|
||||
public class InventoryMonitor implements SetupListener {
|
||||
|
||||
private final Map<NodeAddress, JsonFileManager> jsonFileManagerByNodeAddress = new HashMap<>();
|
||||
private final Map<NodeAddress, List<RequestInfo>> requestInfoListByNode = new HashMap<>();
|
||||
private final File appDir;
|
||||
private final boolean useLocalhostForP2P;
|
||||
private final int intervalSec;
|
||||
private final NetworkNode networkNode;
|
||||
private final GetInventoryRequestManager getInventoryRequestManager;
|
||||
private ArrayList<NodeAddress> seedNodes;
|
||||
private InventoryWebServer inventoryWebServer;
|
||||
private int requestCounter = 0;
|
||||
|
||||
public InventoryMonitor(File appDir,
|
||||
@ -65,10 +65,12 @@ public class InventoryMonitor {
|
||||
BaseCurrencyNetwork network,
|
||||
int intervalSec,
|
||||
int port) {
|
||||
this.appDir = appDir;
|
||||
this.useLocalhostForP2P = useLocalhostForP2P;
|
||||
this.intervalSec = intervalSec;
|
||||
|
||||
setupCapabilities();
|
||||
networkNode = getNetworkNode(appDir);
|
||||
getInventoryRequestManager = new GetInventoryRequestManager(networkNode);
|
||||
|
||||
//TODO until we use all seeds we use our custom seed node file which includes only those which have updated to our branch
|
||||
// Once all seeds are updated we can remove that resource file and prefix
|
||||
@ -76,104 +78,38 @@ public class InventoryMonitor {
|
||||
String networkName = network.name().toLowerCase();
|
||||
String fileName = network.isMainnet() ? "inv_" + networkName : networkName;
|
||||
DefaultSeedNodeRepository.readSeedNodePropertyFile(fileName)
|
||||
.ifPresent(seedNodeFile -> {
|
||||
List<NodeAddress> seedNodes = new ArrayList<>(DefaultSeedNodeRepository.getSeedNodeAddressesFromPropertyFile(fileName));
|
||||
File jsonDir = new File(appDir, "json");
|
||||
if (!jsonDir.exists() && !jsonDir.mkdir()) {
|
||||
log.warn("make jsonDir failed");
|
||||
}
|
||||
seedNodes.forEach(nodeAddress -> {
|
||||
JsonFileManager jsonFileManager = new JsonFileManager(new File(jsonDir, getShortAddress(nodeAddress, useLocalhostForP2P)));
|
||||
jsonFileManagerByNodeAddress.put(nodeAddress, jsonFileManager);
|
||||
});
|
||||
|
||||
NetworkNode networkNode = getNetworkNode(appDir);
|
||||
|
||||
GetInventoryRequestManager getInventoryRequestManager = new GetInventoryRequestManager(networkNode);
|
||||
|
||||
InventoryWebServer inventoryWebServer = new InventoryWebServer(port, seedNodes, seedNodeFile);
|
||||
|
||||
networkNode.start(new SetupListener() {
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
startRequests(inventoryWebServer, getInventoryRequestManager, seedNodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCustomBridges() {
|
||||
}
|
||||
});
|
||||
.ifPresent(bufferedReader -> {
|
||||
seedNodes = new ArrayList<>(DefaultSeedNodeRepository.getSeedNodeAddressesFromPropertyFile(fileName));
|
||||
addJsonFileManagers(seedNodes);
|
||||
inventoryWebServer = new InventoryWebServer(port, seedNodes, bufferedReader);
|
||||
networkNode.start(this);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorNodeReady() {
|
||||
UserThread.runPeriodically(this::requestAllSeeds, intervalSec);
|
||||
requestAllSeeds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHiddenServicePublished() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupFailed(Throwable throwable) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestCustomBridges() {
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown);
|
||||
inventoryWebServer.shutDown();
|
||||
}
|
||||
|
||||
private NetworkNode getNetworkNode(File appDir) {
|
||||
File torDir = new File(appDir, "tor");
|
||||
CoreNetworkProtoResolver networkProtoResolver = new CoreNetworkProtoResolver(Clock.systemDefaultZone());
|
||||
return new NetworkNodeProvider(networkProtoResolver,
|
||||
ArrayList::new,
|
||||
useLocalhostForP2P,
|
||||
9999,
|
||||
torDir,
|
||||
null,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
null,
|
||||
false,
|
||||
false).get();
|
||||
}
|
||||
|
||||
protected void setupCapabilities() {
|
||||
Capabilities.app.addAll(
|
||||
Capability.TRADE_STATISTICS,
|
||||
Capability.TRADE_STATISTICS_2,
|
||||
Capability.ACCOUNT_AGE_WITNESS,
|
||||
Capability.ACK_MSG,
|
||||
Capability.PROPOSAL,
|
||||
Capability.BLIND_VOTE,
|
||||
Capability.DAO_STATE,
|
||||
Capability.BUNDLE_OF_ENVELOPES,
|
||||
Capability.MEDIATION,
|
||||
Capability.SIGNED_ACCOUNT_AGE_WITNESS,
|
||||
Capability.REFUND_AGENT,
|
||||
Capability.TRADE_STATISTICS_HASH_UPDATE,
|
||||
Capability.NO_ADDRESS_PRE_FIX,
|
||||
Capability.TRADE_STATISTICS_3,
|
||||
Capability.RECEIVE_BSQ_BLOCK
|
||||
);
|
||||
}
|
||||
|
||||
private String getShortAddress(NodeAddress nodeAddress, boolean useLocalhostForP2P) {
|
||||
return useLocalhostForP2P ?
|
||||
nodeAddress.getFullAddress().replace(":", "_") :
|
||||
nodeAddress.getFullAddress().substring(0, 10);
|
||||
}
|
||||
|
||||
private void startRequests(InventoryWebServer inventoryWebServer,
|
||||
GetInventoryRequestManager getInventoryRequestManager,
|
||||
List<NodeAddress> seedNodes) {
|
||||
UserThread.runPeriodically(() ->
|
||||
requestAllSeeds(inventoryWebServer, getInventoryRequestManager, seedNodes),
|
||||
intervalSec);
|
||||
|
||||
requestAllSeeds(inventoryWebServer, getInventoryRequestManager, seedNodes);
|
||||
}
|
||||
|
||||
private void requestAllSeeds(InventoryWebServer inventoryWebServer,
|
||||
GetInventoryRequestManager getInventoryRequestManager,
|
||||
List<NodeAddress> seedNodes) {
|
||||
private void requestAllSeeds() {
|
||||
requestCounter++;
|
||||
seedNodes.forEach(nodeAddress -> {
|
||||
RequestInfo requestInfo = new RequestInfo(System.currentTimeMillis());
|
||||
@ -196,7 +132,7 @@ public class InventoryMonitor {
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1))
|
||||
.collect(Collectors.toSet());
|
||||
Map<InventoryItem, Double> averageValues = getAverageValues(requestInfoSetOfOtherNodes);
|
||||
Map<InventoryItem, Double> averageValues = InventoryUtil.getAverageValues(requestInfoSetOfOtherNodes);
|
||||
|
||||
inventoryWebServer.onNewRequestInfo(requestInfoListByNode, averageValues, requestCounter);
|
||||
|
||||
@ -212,51 +148,37 @@ public class InventoryMonitor {
|
||||
});
|
||||
}
|
||||
|
||||
private Map<InventoryItem, Double> getAverageValues(Set<RequestInfo> requestInfoSetOfOtherNodes) {
|
||||
Map<InventoryItem, Double> averageValuesPerItem = new HashMap<>();
|
||||
Arrays.asList(InventoryItem.values()).forEach(inventoryItem -> {
|
||||
if (inventoryItem.getType().equals(Integer.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromIntegerValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Long.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromLongValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Double.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromDoubleValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
}
|
||||
// If type of value is String we ignore it
|
||||
private void addJsonFileManagers(List<NodeAddress> seedNodes) {
|
||||
File jsonDir = new File(appDir, "json");
|
||||
if (!jsonDir.exists() && !jsonDir.mkdir()) {
|
||||
log.warn("make jsonDir failed");
|
||||
}
|
||||
seedNodes.forEach(nodeAddress -> {
|
||||
JsonFileManager jsonFileManager = new JsonFileManager(new File(jsonDir, getShortAddress(nodeAddress, useLocalhostForP2P)));
|
||||
jsonFileManagerByNodeAddress.put(nodeAddress, jsonFileManager);
|
||||
});
|
||||
return averageValuesPerItem;
|
||||
}
|
||||
|
||||
private double getAverageFromIntegerValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Integer.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToInt(inventory -> Integer.parseInt(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
private NetworkNode getNetworkNode(File appDir) {
|
||||
File torDir = new File(appDir, "tor");
|
||||
CoreNetworkProtoResolver networkProtoResolver = new CoreNetworkProtoResolver(Clock.systemDefaultZone());
|
||||
return new NetworkNodeProvider(networkProtoResolver,
|
||||
ArrayList::new,
|
||||
useLocalhostForP2P,
|
||||
9999,
|
||||
torDir,
|
||||
null,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
null,
|
||||
false,
|
||||
false).get();
|
||||
}
|
||||
|
||||
private double getAverageFromLongValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Long.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToLong(inventory -> Long.parseLong(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
private double getAverageFromDoubleValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Double.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem))))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
private String getShortAddress(NodeAddress nodeAddress, boolean useLocalhostForP2P) {
|
||||
return useLocalhostForP2P ?
|
||||
nodeAddress.getFullAddress().replace(":", "_") :
|
||||
nodeAddress.getFullAddress().substring(0, 10);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package bisq.inventory;
|
||||
import bisq.core.locale.Res;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.AsciiLogo;
|
||||
import bisq.common.app.Log;
|
||||
import bisq.common.app.Version;
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
@ -51,7 +52,8 @@ public class InventoryMonitorMain {
|
||||
|
||||
// prog args for regtest: 10 1 BTC_REGTEST
|
||||
public static void main(String[] args) {
|
||||
int intervalSec = 600;
|
||||
// Default values
|
||||
int intervalSec = 300;
|
||||
boolean useLocalhostForP2P = false;
|
||||
BaseCurrencyNetwork network = BaseCurrencyNetwork.BTC_MAINNET;
|
||||
int port = 80;
|
||||
@ -69,16 +71,27 @@ public class InventoryMonitorMain {
|
||||
port = Integer.parseInt(args[3]);
|
||||
}
|
||||
|
||||
String appName = "bisq-InventoryMonitor-" + network;
|
||||
|
||||
String appName = "bisq-InventoryMonitor-" + network + "-" + intervalSec;
|
||||
File appDir = new File(Utilities.getUserDataDir(), appName);
|
||||
if (!appDir.exists() && !appDir.mkdir()) {
|
||||
log.warn("make appDir failed");
|
||||
}
|
||||
inventoryMonitor = new InventoryMonitor(appDir, useLocalhostForP2P, network, intervalSec, port);
|
||||
|
||||
setup(network, appDir);
|
||||
}
|
||||
|
||||
private static void setup(BaseCurrencyNetwork network, File appDir) {
|
||||
AsciiLogo.showAsciiLogo();
|
||||
String logPath = Paths.get(appDir.getPath(), "bisq").toString();
|
||||
Log.setup(logPath);
|
||||
Log.setLevel(Level.INFO);
|
||||
Version.setBaseCryptoNetworkId(network.ordinal());
|
||||
Res.setup();
|
||||
|
||||
inventoryMonitor = new InventoryMonitor(appDir, useLocalhostForP2P, network, intervalSec, port);
|
||||
Res.setup(); // Used for some formatting in the webserver
|
||||
|
||||
// We do not set any capabilities as we don't want to receive any network data beside our response.
|
||||
// We also do not use capabilities for the request/response messages as we only connect to seeds nodes and
|
||||
|
||||
ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat(inventoryMonitor.getClass().getSimpleName())
|
||||
|
124
inventory/src/main/java/bisq/inventory/InventoryUtil.java
Normal file
124
inventory/src/main/java/bisq/inventory/InventoryUtil.java
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.inventory;
|
||||
|
||||
import bisq.core.network.p2p.inventory.DeviationSeverity;
|
||||
import bisq.core.network.p2p.inventory.InventoryItem;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class InventoryUtil {
|
||||
|
||||
public static Map<InventoryItem, Double> getAverageValues(Set<RequestInfo> requestInfoSetOfOtherNodes) {
|
||||
Map<InventoryItem, Double> averageValuesPerItem = new HashMap<>();
|
||||
Arrays.asList(InventoryItem.values()).forEach(inventoryItem -> {
|
||||
if (inventoryItem.getType().equals(Integer.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromIntegerValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Long.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromLongValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
} else if (inventoryItem.getType().equals(Double.class)) {
|
||||
averageValuesPerItem.put(inventoryItem, getAverageFromDoubleValues(requestInfoSetOfOtherNodes, inventoryItem));
|
||||
}
|
||||
// If type of value is String we ignore it
|
||||
});
|
||||
return averageValuesPerItem;
|
||||
}
|
||||
|
||||
public static double getAverageFromIntegerValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Integer.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToInt(inventory -> Integer.parseInt(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static double getAverageFromLongValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Long.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToLong(inventory -> Long.parseLong(inventory.get(inventoryItem)))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static double getAverageFromDoubleValues(Set<RequestInfo> requestInfoSetOfOtherNodes,
|
||||
InventoryItem inventoryItem) {
|
||||
checkArgument(inventoryItem.getType().equals(Double.class));
|
||||
return requestInfoSetOfOtherNodes.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem))))
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
||||
public static DeviationSeverity getDeviationSeverityForHash(Map<NodeAddress, List<RequestInfo>> map,
|
||||
String daoStateChainHeightAsString,
|
||||
RequestInfo requestInfo,
|
||||
InventoryItem daoStateHash) {
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
Map<String, Integer> sameHashesPerHash = new HashMap<>();
|
||||
map.values().stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.filter(e -> e.get(InventoryItem.daoStateChainHeight).equals(daoStateChainHeightAsString))
|
||||
.map(e -> e.get(daoStateHash))
|
||||
.forEach(e -> {
|
||||
sameHashesPerHash.putIfAbsent(e, 0);
|
||||
int prev = sameHashesPerHash.get(e);
|
||||
sameHashesPerHash.put(e, prev + 1);
|
||||
});
|
||||
if (sameHashesPerHash.size() > 1) {
|
||||
List<Tuple2<String, Integer>> sameHashesPerHashList = new ArrayList<>();
|
||||
sameHashesPerHash.forEach((key, value) -> sameHashesPerHashList.add(new Tuple2<>(key, value)));
|
||||
sameHashesPerHashList.sort(Comparator.comparing(o -> o.second));
|
||||
Collections.reverse(sameHashesPerHashList);
|
||||
|
||||
// It could be that first and any following list entry has same number of hashes, but we ignore that as
|
||||
// it is reason enough to alert the operators in case not all hashes are the same.
|
||||
if (sameHashesPerHashList.get(0).first.equals(requestInfo.getInventory().get(daoStateHash))) {
|
||||
// We are in the majority group.
|
||||
// We also set a warning to make sure the operators act quickly and to check if there are
|
||||
// more severe issues.
|
||||
deviationSeverity = DeviationSeverity.WARN;
|
||||
} else {
|
||||
deviationSeverity = DeviationSeverity.ALERT;
|
||||
}
|
||||
}
|
||||
return deviationSeverity;
|
||||
}
|
||||
}
|
@ -57,16 +57,6 @@ public class InventoryWebServer {
|
||||
this.seedNodes = seedNodes;
|
||||
setupOperatorMap(seedNodeFile);
|
||||
|
||||
setupServer(port);
|
||||
}
|
||||
|
||||
public void onNewRequestInfo(Map<NodeAddress, List<RequestInfo>> requestInfoListByNode,
|
||||
Map<InventoryItem, Double> averageValues, int requestCounter) {
|
||||
this.requestCounter = requestCounter;
|
||||
html = getHtml(requestInfoListByNode, averageValues);
|
||||
}
|
||||
|
||||
private void setupServer(int port) {
|
||||
Spark.port(port);
|
||||
Spark.get("/", (req, res) -> {
|
||||
log.info("Incoming request from: {}", req.userAgent());
|
||||
@ -74,19 +64,19 @@ public class InventoryWebServer {
|
||||
});
|
||||
}
|
||||
|
||||
private void setupOperatorMap(BufferedReader seedNodeFile) {
|
||||
seedNodeFile.lines().forEach(line -> {
|
||||
if (!line.startsWith("#")) {
|
||||
String[] strings = line.split(" \\(@");
|
||||
String node = strings.length > 0 ? strings[0] : "n/a";
|
||||
String operator = strings.length > 1 ? strings[1].replace(")", "") : "n/a";
|
||||
operatorByNodeAddress.put(node, operator);
|
||||
}
|
||||
});
|
||||
public void onNewRequestInfo(Map<NodeAddress, List<RequestInfo>> requestInfoListByNode,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
int requestCounter) {
|
||||
this.requestCounter = requestCounter;
|
||||
html = generateHtml(requestInfoListByNode, averageValues);
|
||||
}
|
||||
|
||||
private String getHtml(Map<NodeAddress, List<RequestInfo>> map,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
public void shutDown() {
|
||||
Spark.stop();
|
||||
}
|
||||
|
||||
private String generateHtml(Map<NodeAddress, List<RequestInfo>> map,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<html>" +
|
||||
"<head><style>table, th, td {border: 1px solid black;}</style></head>" +
|
||||
@ -110,7 +100,7 @@ public class InventoryWebServer {
|
||||
.append("<td>").append(getSeedNodeInfo(seedNode, requestInfo, averageValues)).append("</td>")
|
||||
.append("<td>").append(getRequestInfo(requestInfo, numRequests)).append("</td>")
|
||||
.append("<td>").append(getDataInfo(requestInfo, averageValues)).append("</td>")
|
||||
.append("<td>").append(getDaoInfo(requestInfo, averageValues)).append("</td>")
|
||||
.append("<td>").append(getDaoInfo(requestInfo, averageValues, map)).append("</td>")
|
||||
.append("<td>").append(getNetworkInfo(requestInfo, averageValues)).append("</td>");
|
||||
html.append("</tr>");
|
||||
} else {
|
||||
@ -128,7 +118,6 @@ public class InventoryWebServer {
|
||||
return html.toString();
|
||||
}
|
||||
|
||||
|
||||
private String getSeedNodeInfo(NodeAddress nodeAddress,
|
||||
@Nullable RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
@ -144,32 +133,32 @@ public class InventoryWebServer {
|
||||
addInventoryItem("Version: ", requestInfo, sb, InventoryItem.version);
|
||||
addInventoryItem("Memory used: ", requestInfo, averageValues, sb, InventoryItem.usedMemory,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
addInventoryItem("Node started at: ",
|
||||
String jvmStartTimeString = addInventoryItem("Node started at: ",
|
||||
requestInfo,
|
||||
null,
|
||||
sb,
|
||||
InventoryItem.jvmStartTime,
|
||||
value -> new Date(Long.parseLong(value)).toString());
|
||||
|
||||
long jvmStartTime = Long.parseLong(requestInfo.getInventory().get(InventoryItem.jvmStartTime));
|
||||
String duration = FormattingUtils.formatDurationAsWords(System.currentTimeMillis() - jvmStartTime, true, true);
|
||||
String duration = FormattingUtils.formatDurationAsWords(
|
||||
System.currentTimeMillis() - Long.parseLong(jvmStartTimeString),
|
||||
true, true);
|
||||
sb.append("Run duration: ").append(duration).append("<br/>");
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getRequestInfo(RequestInfo last, int numRequests) {
|
||||
private String getRequestInfo(RequestInfo requestInfo, int numRequests) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Date requestStartTime = new Date(last.getRequestStartTime());
|
||||
Date requestStartTime = new Date(requestInfo.getRequestStartTime());
|
||||
sb.append("Requested at: ").append(requestStartTime).append("<br/>");
|
||||
|
||||
Date responseTime = new Date(last.getResponseTime());
|
||||
Date responseTime = new Date(requestInfo.getResponseTime());
|
||||
sb.append("Response received at: ").append(responseTime).append("<br/>");
|
||||
|
||||
long rrt = last.getResponseTime() - last.getRequestStartTime();
|
||||
long rrt = requestInfo.getResponseTime() - requestInfo.getRequestStartTime();
|
||||
DeviationSeverity rrtDeviationSeverity = DeviationSeverity.OK;
|
||||
if (rrt > 20_000) {
|
||||
rrtDeviationSeverity = DeviationSeverity.ALERT;
|
||||
@ -184,7 +173,7 @@ public class InventoryWebServer {
|
||||
sb.append("Number of requests: ").append(getColorTagByDeviationSeverity(DeviationSeverity.OK))
|
||||
.append(numRequests).append(CLOSE_TAG);
|
||||
|
||||
String errorMessage = last.getErrorMessage();
|
||||
String errorMessage = requestInfo.getErrorMessage();
|
||||
rrtDeviationSeverity = errorMessage == null || errorMessage.isEmpty() ?
|
||||
DeviationSeverity.OK :
|
||||
DeviationSeverity.WARN;
|
||||
@ -194,99 +183,141 @@ public class InventoryWebServer {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private String getDataInfo(RequestInfo last,
|
||||
private String getDataInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.OfferPayload);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.MailboxStoragePayload);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.TradeStatistics3);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.Alert);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.Filter);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.Mediator);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.RefundAgent);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.AccountAgeWitness);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.SignedWitness);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.OfferPayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.MailboxStoragePayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.TradeStatistics3);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.Alert);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.Filter);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.Mediator);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.RefundAgent);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.AccountAgeWitness);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.SignedWitness);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getDaoInfo(RequestInfo last,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
private String getDaoInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
addInventoryItem("Number of BSQ blocks: ", last, averageValues, sb, InventoryItem.numBsqBlocks);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.TempProposalPayload);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.ProposalPayload);
|
||||
addInventoryItem(last, averageValues, sb, InventoryItem.BlindVotePayload);
|
||||
addInventoryItem("DAO state block height: ", last, averageValues, sb, InventoryItem.daoStateChainHeight);
|
||||
addInventoryItem("DAO state hash: ", last, sb, InventoryItem.daoStateHash);
|
||||
addInventoryItem("Proposal state hash: ", last, sb, InventoryItem.proposalHash);
|
||||
addInventoryItem("Blind vote state hash: ", last, sb, InventoryItem.blindVoteHash);
|
||||
addInventoryItem("Number of BSQ blocks: ", requestInfo, averageValues, sb, InventoryItem.numBsqBlocks);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.TempProposalPayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.ProposalPayload);
|
||||
addInventoryItem(requestInfo, averageValues, sb, InventoryItem.BlindVotePayload);
|
||||
String daoStateChainHeightAsString = addInventoryItem("DAO state block height: ", requestInfo,
|
||||
averageValues, sb, InventoryItem.daoStateChainHeight);
|
||||
|
||||
DeviationSeverity daoStateHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.daoStateHash);
|
||||
addInventoryItem("DAO state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.daoStateHash, null, daoStateHashDeviationSeverity);
|
||||
|
||||
// The hash for proposal changes only at first block of blind vote phase but as we do not want to initialize the
|
||||
// dao domain we cannot check that. But we also don't need that as we can just compare that all hashes at all
|
||||
// blocks from all seeds are the same. Same for blindVoteHash.
|
||||
|
||||
DeviationSeverity proposalHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.proposalHash);
|
||||
addInventoryItem("Proposal state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.proposalHash, null, proposalHashDeviationSeverity);
|
||||
|
||||
DeviationSeverity blindVoteHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map,
|
||||
daoStateChainHeightAsString,
|
||||
requestInfo,
|
||||
InventoryItem.blindVoteHash);
|
||||
addInventoryItem("Blind vote state hash: ", requestInfo, null, sb,
|
||||
InventoryItem.blindVoteHash, null, blindVoteHashDeviationSeverity);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getNetworkInfo(RequestInfo last,
|
||||
private String getNetworkInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
addInventoryItem("Max. connections: ", last, averageValues, sb, InventoryItem.maxConnections);
|
||||
addInventoryItem("Number of connections: ", last, averageValues, sb, InventoryItem.numConnections);
|
||||
addInventoryItem("Max. connections: ", requestInfo, averageValues, sb, InventoryItem.maxConnections);
|
||||
addInventoryItem("Number of connections: ", requestInfo, averageValues, sb, InventoryItem.numConnections);
|
||||
|
||||
addInventoryItem("Sent messages/sec: ", last, averageValues, sb, InventoryItem.sentMessagesPerSec,
|
||||
addInventoryItem("Sent messages/sec: ", requestInfo, averageValues, sb, InventoryItem.sentMessagesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Received messages/sec: ", last, averageValues, sb, InventoryItem.receivedMessagesPerSec,
|
||||
addInventoryItem("Received messages/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedMessagesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Sent bytes/sec: ", last, averageValues, sb, InventoryItem.sentBytesPerSec,
|
||||
addInventoryItem("Sent bytes/sec: ", requestInfo, averageValues, sb, InventoryItem.sentBytesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Received bytes/sec: ", last, averageValues, sb, InventoryItem.receivedBytesPerSec,
|
||||
addInventoryItem("Received bytes/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedBytesPerSec,
|
||||
value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)));
|
||||
addInventoryItem("Sent data: ", last, averageValues, sb, InventoryItem.sentBytes,
|
||||
addInventoryItem("Sent data: ", requestInfo, averageValues, sb, InventoryItem.sentBytes,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
addInventoryItem("Received data: ", last, averageValues, sb, InventoryItem.receivedBytes,
|
||||
addInventoryItem("Received data: ", requestInfo, averageValues, sb, InventoryItem.receivedBytes,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value)));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void addInventoryItem(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
addInventoryItem("Number of " + inventoryItem.getKey() + ": ",
|
||||
private String addInventoryItem(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem("Number of " + inventoryItem.getKey() + ": ",
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
inventoryItem);
|
||||
}
|
||||
|
||||
private void addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
addInventoryItem(title,
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem(title,
|
||||
requestInfo,
|
||||
null,
|
||||
sb,
|
||||
inventoryItem);
|
||||
}
|
||||
|
||||
private void addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
addInventoryItem(title,
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem) {
|
||||
return addInventoryItem(title,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
inventoryItem,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
private void addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem,
|
||||
@Nullable Function<String, String> formatter) {
|
||||
String valueAsString;
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem,
|
||||
@Nullable Function<String, String> formatter) {
|
||||
return addInventoryItem(title,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
sb,
|
||||
inventoryItem,
|
||||
formatter,
|
||||
null);
|
||||
}
|
||||
|
||||
private String addInventoryItem(String title,
|
||||
RequestInfo requestInfo,
|
||||
@Nullable Map<InventoryItem, Double> averageValues,
|
||||
StringBuilder sb,
|
||||
InventoryItem inventoryItem,
|
||||
@Nullable Function<String, String> formatter,
|
||||
@Nullable DeviationSeverity deviationSeverity) {
|
||||
String valueAsString = null;
|
||||
String displayString = "n/a";
|
||||
String deviationAsString = "";
|
||||
String colorTag = getColorTagByDeviationSeverity(DeviationSeverity.OK);
|
||||
if (requestInfo.getInventory().containsKey(inventoryItem)) {
|
||||
@ -311,15 +342,20 @@ public class InventoryWebServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (deviationSeverity != null) {
|
||||
colorTag = getColorTagByDeviationSeverity(deviationSeverity);
|
||||
}
|
||||
|
||||
// We only do formatting if we have any value
|
||||
if (formatter != null) {
|
||||
valueAsString = formatter.apply(valueAsString);
|
||||
displayString = formatter.apply(valueAsString);
|
||||
} else {
|
||||
displayString = valueAsString;
|
||||
}
|
||||
} else {
|
||||
valueAsString = "n/a";
|
||||
}
|
||||
|
||||
sb.append(title).append(colorTag).append(valueAsString).append(deviationAsString).append(CLOSE_TAG);
|
||||
sb.append(title).append(colorTag).append(displayString).append(deviationAsString).append(CLOSE_TAG);
|
||||
return valueAsString;
|
||||
}
|
||||
|
||||
private String getColorTagByDeviationSeverity(DeviationSeverity deviationSeverity) {
|
||||
@ -333,4 +369,15 @@ public class InventoryWebServer {
|
||||
return "<font color=\"black\">";
|
||||
}
|
||||
}
|
||||
|
||||
private void setupOperatorMap(BufferedReader seedNodeFile) {
|
||||
seedNodeFile.lines().forEach(line -> {
|
||||
if (!line.startsWith("#")) {
|
||||
String[] strings = line.split(" \\(@");
|
||||
String node = strings.length > 0 ? strings[0] : "n/a";
|
||||
String operator = strings.length > 1 ? strings[1].replace(")", "") : "n/a";
|
||||
operatorByNodeAddress.put(node, operator);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -10,5 +10,3 @@ s67qglwhkgkyvr74.onion:8000 (@emzy)
|
||||
sn2bisqad7ncazupgbd3dcedqh5ptirgwofw63djwpdtftwhddo75oid.onion:8000 (@miker)
|
||||
sn3bsq3evqkpshdmc3sbdxafkhfnk7ctop44jsxbxyys5ridsaw5abyd.onion:8000 (@miker)
|
||||
sn4bsqpc7eb2ntvpsycxbzqt6fre72l4krp2fl5svphfh2eusrqtq3qd.onion:8000 (@miker)
|
||||
5quyxpxheyvzmb2d.onion:8000 (@miker)
|
||||
rm7b56wbrcczpjvl.onion:8000 (@miker)
|
||||
|
Loading…
Reference in New Issue
Block a user