From f1fdf3c0a54cc5251268c9d300aadae46da65d29 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 22 Oct 2020 23:46:13 -0500 Subject: [PATCH] Improve deviation model --- .../inventory/GetInventoryRequestHandler.java | 12 +- .../inventory/GetInventoryRequestManager.java | 2 + .../p2p/inventory/GetInventoryRequester.java | 1 + .../network/p2p/inventory/InventoryItem.java | 97 ----- .../messages/GetInventoryResponse.java | 2 +- .../network/p2p/inventory/model/Average.java | 46 +++ .../model/DeviationByIntegerDiff.java | 83 ++++ .../model/DeviationByPercentage.java | 47 +++ .../inventory/model/DeviationOfHashes.java | 75 ++++ .../{ => model}/DeviationSeverity.java | 2 +- .../p2p/inventory/model/DeviationType.java | 21 + .../p2p/inventory/model/InventoryItem.java | 178 +++++++++ .../p2p/inventory/model}/RequestInfo.java | 16 +- .../java/bisq/inventory/InventoryMonitor.java | 47 ++- .../java/bisq/inventory/InventoryUtil.java | 177 --------- .../bisq/inventory/InventoryWebServer.java | 361 ++++++++---------- .../src/main/java/bisq/seednode/SeedNode.java | 3 - 17 files changed, 665 insertions(+), 505 deletions(-) delete mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/InventoryItem.java create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/Average.java create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByIntegerDiff.java create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByPercentage.java create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java rename core/src/main/java/bisq/core/network/p2p/inventory/{ => model}/DeviationSeverity.java (94%) create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationType.java create mode 100644 core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java rename {inventory/src/main/java/bisq/inventory => core/src/main/java/bisq/core/network/p2p/inventory/model}/RequestInfo.java (72%) delete mode 100644 inventory/src/main/java/bisq/inventory/InventoryUtil.java diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java index cbfe1a5edf8..5211077ab24 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestHandler.java @@ -26,6 +26,7 @@ import bisq.core.dao.state.DaoStateService; import bisq.core.network.p2p.inventory.messages.GetInventoryRequest; import bisq.core.network.p2p.inventory.messages.GetInventoryResponse; +import bisq.core.network.p2p.inventory.model.InventoryItem; import bisq.network.p2p.network.Connection; import bisq.network.p2p.network.MessageListener; @@ -48,11 +49,9 @@ import com.google.common.base.Optional; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.lang.management.ManagementFactory; @@ -68,7 +67,6 @@ public class GetInventoryRequestHandler implements MessageListener { private final ProposalStateMonitoringService proposalStateMonitoringService; private final BlindVoteStateMonitoringService blindVoteStateMonitoringService; private final int maxConnections; - private final Set permittedRequestersPubKey = new HashSet<>(); @Inject public GetInventoryRequestHandler(NetworkNode networkNode, @@ -94,10 +92,6 @@ public GetInventoryRequestHandler(NetworkNode networkNode, @Override public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { if (networkEnvelope instanceof GetInventoryRequest) { - if (permittedRequestersPubKey.isEmpty()) { - return; - } - // Data GetInventoryRequest getInventoryRequest = (GetInventoryRequest) networkEnvelope; Map dataObjects = new HashMap<>(); @@ -179,8 +173,4 @@ public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) { public void shutDown() { networkNode.removeMessageListener(this); } - - public void addPermittedRequestersPubKey(String pubKey) { - permittedRequestersPubKey.add(pubKey); - } } diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestManager.java b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestManager.java index 68c6f269c07..816bb786fee 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestManager.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequestManager.java @@ -17,6 +17,8 @@ package bisq.core.network.p2p.inventory; +import bisq.core.network.p2p.inventory.model.InventoryItem; + import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.NetworkNode; diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequester.java b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequester.java index 0cc109476e7..a4961e824dc 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequester.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/GetInventoryRequester.java @@ -19,6 +19,7 @@ import bisq.core.network.p2p.inventory.messages.GetInventoryRequest; import bisq.core.network.p2p.inventory.messages.GetInventoryResponse; +import bisq.core.network.p2p.inventory.model.InventoryItem; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.network.CloseConnectionReason; diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/InventoryItem.java b/core/src/main/java/bisq/core/network/p2p/inventory/InventoryItem.java deleted file mode 100644 index 29fff373bd4..00000000000 --- a/core/src/main/java/bisq/core/network/p2p/inventory/InventoryItem.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 . - */ - -package bisq.core.network.p2p.inventory; - -import lombok.Getter; - -public enum InventoryItem { - OfferPayload("OfferPayload", Integer.class), - MailboxStoragePayload("MailboxStoragePayload", Integer.class, 0.9, 1.1, 0.95, 1.05), - TradeStatistics3("TradeStatistics3", Integer.class, 0.9, 1.1, 0.95, 1.05), - Alert("Alert", Integer.class), - Filter("Filter", Integer.class), - Mediator("Mediator", Integer.class), - RefundAgent("RefundAgent", Integer.class), - AccountAgeWitness("AccountAgeWitness", Integer.class, 0.9, 1.1, 0.95, 1.05), - SignedWitness("SignedWitness", Integer.class, 0.9, 1.1, 0.95, 1.05), - - TempProposalPayload("TempProposalPayload", Integer.class), - ProposalPayload("ProposalPayload", Integer.class), - BlindVotePayload("BlindVotePayload", Integer.class), - - daoStateChainHeight("daoStateChainHeight", Integer.class), - numBsqBlocks("numBsqBlocks", Integer.class), - daoStateHash("daoStateHash", String.class), - proposalHash("proposalHash", String.class), - blindVoteHash("blindVoteHash", String.class), - - maxConnections("maxConnections", Integer.class, 0.33, 3, 0.4, 2.5), - numConnections("numConnections", Integer.class, 0.33, 3, 0.4, 2.5), - peakNumConnections("peakNumConnections", Integer.class, 0.33, 3, 0.4, 2.5), - numAllConnectionsLostEvents("numAllConnectionsLostEvents", Integer.class, 0.9, 1.1, 0.95, 1.05), - sentBytes("sentBytes", Long.class, 0, 5, 0, 4), - sentBytesPerSec("sentBytesPerSec", Double.class, 0, 3, 0, 2), - receivedBytes("receivedBytes", Long.class, 0, 5, 0, 4), - receivedBytesPerSec("receivedBytesPerSec", Double.class, 0, 3, 0, 2), - receivedMessagesPerSec("receivedMessagesPerSec", Double.class, 0, 3, 0, 2), - sentMessagesPerSec("sentMessagesPerSec", Double.class, 0, 3, 0, 2), - - version("version", String.class), - usedMemory("usedMemory", Long.class, 0, 3, 0, 2), - jvmStartTime("jvmStartTime", Long.class); - - @Getter - private final String key; - @Getter - private final Class type; - private double lowerAlertTrigger = 0.7; - private double upperAlertTrigger = 1.5; - private double lowerWarnTrigger = 0.8; - private double upperWarnTrigger = 1.3; - - InventoryItem(String key, Class type) { - this.key = key; - this.type = type; - } - - InventoryItem(String key, - Class type, - double lowerAlertTrigger, - double upperAlertTrigger, - double lowerWarnTrigger, - double upperWarnTrigger) { - this.key = key; - this.type = type; - this.lowerAlertTrigger = lowerAlertTrigger; - this.upperAlertTrigger = upperAlertTrigger; - this.lowerWarnTrigger = lowerWarnTrigger; - this.upperWarnTrigger = upperWarnTrigger; - } - - public DeviationSeverity getDeviationSeverity(double deviation) { - if (deviation <= lowerAlertTrigger || deviation >= upperAlertTrigger) { - return DeviationSeverity.ALERT; - } - - if (deviation <= lowerWarnTrigger || deviation >= upperWarnTrigger) { - return DeviationSeverity.WARN; - } - - return DeviationSeverity.OK; - } -} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/messages/GetInventoryResponse.java b/core/src/main/java/bisq/core/network/p2p/inventory/messages/GetInventoryResponse.java index e4ae5d6d4ba..b9c5cf9ce34 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/messages/GetInventoryResponse.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/messages/GetInventoryResponse.java @@ -17,7 +17,7 @@ package bisq.core.network.p2p.inventory.messages; -import bisq.core.network.p2p.inventory.InventoryItem; +import bisq.core.network.p2p.inventory.model.InventoryItem; import bisq.common.app.Version; import bisq.common.proto.network.NetworkEnvelope; diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/Average.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/Average.java new file mode 100644 index 00000000000..3ed4a183103 --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/Average.java @@ -0,0 +1,46 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class Average { + public static Map of(Set requestInfoSet) { + Map averageValuesPerItem = new HashMap<>(); + Arrays.asList(InventoryItem.values()).forEach(inventoryItem -> { + if (inventoryItem.isNumberValue()) { + averageValuesPerItem.put(inventoryItem, getAverage(requestInfoSet, inventoryItem)); + } + }); + return averageValuesPerItem; + } + + public static double getAverage(Set requestInfoSet, InventoryItem inventoryItem) { + return requestInfoSet.stream() + .map(RequestInfo::getInventory) + .filter(Objects::nonNull) + .filter(inventory -> inventory.containsKey(inventoryItem)) + .mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem)))) + .average() + .orElse(0d); + } +} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByIntegerDiff.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByIntegerDiff.java new file mode 100644 index 00000000000..f824df87179 --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByIntegerDiff.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +import bisq.common.util.Tuple2; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +public class DeviationByIntegerDiff implements DeviationType { + private final int warnTrigger; + private final int alertTrigger; + + public DeviationByIntegerDiff(int warnTrigger, int alertTrigger) { + this.warnTrigger = warnTrigger; + this.alertTrigger = alertTrigger; + } + + public DeviationSeverity getDeviationSeverity(Collection> collection, + @Nullable String value, + InventoryItem inventoryItem) { + DeviationSeverity deviationSeverity = DeviationSeverity.OK; + if (value == null) { + return deviationSeverity; + } + + Map sameItemsByValue = new HashMap<>(); + collection.stream() + .filter(list -> !list.isEmpty()) + .map(list -> list.get(list.size() - 1)) // We use last item only + .map(RequestInfo::getInventory) + .filter(Objects::nonNull) + .map(e -> e.get(inventoryItem)) + .forEach(e -> { + sameItemsByValue.putIfAbsent(e, 0); + int prev = sameItemsByValue.get(e); + sameItemsByValue.put(e, prev + 1); + }); + if (sameItemsByValue.size() > 1) { + List> sameItems = new ArrayList<>(); + sameItemsByValue.forEach((k, v) -> sameItems.add(new Tuple2<>(k, v))); + sameItems.sort(Comparator.comparing(o -> o.second)); + Collections.reverse(sameItems); + String majority = sameItems.get(0).first; + if (!majority.equals(value)) { + int majorityAsInt = Integer.parseInt(majority); + int valueAsInt = Integer.parseInt(value); + int diff = Math.abs(majorityAsInt - valueAsInt); + if (diff >= alertTrigger) { + deviationSeverity = DeviationSeverity.ALERT; + } else if (diff >= warnTrigger) { + deviationSeverity = DeviationSeverity.WARN; + } else { + deviationSeverity = DeviationSeverity.OK; + } + } + } + return deviationSeverity; + } +} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByPercentage.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByPercentage.java new file mode 100644 index 00000000000..60093d2e83a --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationByPercentage.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +public class DeviationByPercentage implements DeviationType { + private final double lowerAlertTrigger; + private final double upperAlertTrigger; + private final double lowerWarnTrigger; + private final double upperWarnTrigger; + + public DeviationByPercentage(double lowerAlertTrigger, + double upperAlertTrigger, + double lowerWarnTrigger, + double upperWarnTrigger) { + this.lowerAlertTrigger = lowerAlertTrigger; + this.upperAlertTrigger = upperAlertTrigger; + this.lowerWarnTrigger = lowerWarnTrigger; + this.upperWarnTrigger = upperWarnTrigger; + } + + public DeviationSeverity getDeviationSeverity(double deviation) { + if (deviation <= lowerAlertTrigger || deviation >= upperAlertTrigger) { + return DeviationSeverity.ALERT; + } + + if (deviation <= lowerWarnTrigger || deviation >= upperWarnTrigger) { + return DeviationSeverity.WARN; + } + + return DeviationSeverity.OK; + } +} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java new file mode 100644 index 00000000000..d1a9efcb307 --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationOfHashes.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +import bisq.common.util.Tuple2; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + +public class DeviationOfHashes implements DeviationType { + public DeviationSeverity getDeviationSeverity(Collection> collection, + @Nullable String value, + InventoryItem inventoryItem, + String currentBlockHeight) { + DeviationSeverity deviationSeverity = DeviationSeverity.OK; + if (value == null) { + return deviationSeverity; + } + + Map sameHashesPerHashListByHash = new HashMap<>(); + collection.stream() + .filter(list -> !list.isEmpty()) + .map(list -> list.get(list.size() - 1)) // We use last item only + .map(RequestInfo::getInventory) + .filter(Objects::nonNull) + .filter(inventory -> inventory.get(InventoryItem.daoStateChainHeight).equals(currentBlockHeight)) + .map(inventory -> inventory.get(inventoryItem)) + .forEach(v -> { + sameHashesPerHashListByHash.putIfAbsent(v, 0); + int prev = sameHashesPerHashListByHash.get(v); + sameHashesPerHashListByHash.put(v, prev + 1); + }); + if (sameHashesPerHashListByHash.size() > 1) { + List> sameHashesPerHashList = new ArrayList<>(); + sameHashesPerHashListByHash.forEach((k, v) -> sameHashesPerHashList.add(new Tuple2<>(k, v))); + 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(value)) { + // 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; + } +} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/DeviationSeverity.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationSeverity.java similarity index 94% rename from core/src/main/java/bisq/core/network/p2p/inventory/DeviationSeverity.java rename to core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationSeverity.java index 0efdd1369ee..2c40937425c 100644 --- a/core/src/main/java/bisq/core/network/p2p/inventory/DeviationSeverity.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationSeverity.java @@ -15,7 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.core.network.p2p.inventory; +package bisq.core.network.p2p.inventory.model; public enum DeviationSeverity { OK, diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationType.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationType.java new file mode 100644 index 00000000000..565292eee70 --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/DeviationType.java @@ -0,0 +1,21 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +public interface DeviationType { +} diff --git a/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java new file mode 100644 index 00000000000..a978727f5b4 --- /dev/null +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/InventoryItem.java @@ -0,0 +1,178 @@ +/* + * 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 . + */ + +package bisq.core.network.p2p.inventory.model; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import lombok.Getter; + +import org.jetbrains.annotations.Nullable; + +public enum InventoryItem { + // Percentage deviation + OfferPayload("OfferPayload", + true, + new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)), + MailboxStoragePayload("MailboxStoragePayload", + true, + new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)), + TradeStatistics3("MailboxStoragePayload", + true, + new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)), + AccountAgeWitness("MailboxStoragePayload", + true, + new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)), + SignedWitness("MailboxStoragePayload", + true, + new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)), + + // Should be same value + Alert("Alert", + true, + new DeviationByIntegerDiff(1, 1)), + Filter("Filter", + true, + new DeviationByIntegerDiff(1, 1)), + Mediator("Mediator", + true, + new DeviationByIntegerDiff(1, 1)), + RefundAgent("RefundAgent", + true, + new DeviationByIntegerDiff(1, 1)), + + // Should be very close values + TempProposalPayload("TempProposalPayload", + true, + new DeviationByIntegerDiff(3, 5)), + ProposalPayload("ProposalPayload", + true, + new DeviationByIntegerDiff(1, 2)), + BlindVotePayload("BlindVotePayload", + true, + new DeviationByIntegerDiff(1, 2)), + + // Should be very close values + daoStateChainHeight("daoStateChainHeight", + true, + new DeviationByIntegerDiff(1, 3)), + numBsqBlocks("numBsqBlocks", + true, + new DeviationByIntegerDiff(1, 3)), + + // Has to be same values at same block + daoStateHash("daoStateHash", + false, + new DeviationOfHashes()), + proposalHash("proposalHash", + false, + new DeviationOfHashes()), + blindVoteHash("blindVoteHash", + false, + new DeviationOfHashes()), + + // Percentage deviation + maxConnections("maxConnections", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + numConnections("numConnections", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + peakNumConnections("peakNumConnections", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + numAllConnectionsLostEvents("numAllConnectionsLostEvents", + true, + new DeviationByIntegerDiff(1, 2)), + sentBytesPerSec("sentBytesPerSec", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + receivedBytesPerSec("receivedBytesPerSec", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + receivedMessagesPerSec("receivedMessagesPerSec", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + sentMessagesPerSec("sentMessagesPerSec", + true, + new DeviationByPercentage(0.33, 3, 0.4, 2.5)), + + // No deviation check + sentBytes("sentBytes", true), + receivedBytes("receivedBytes", true), + + // No deviation check + version("version", false), + usedMemory("usedMemory", true), + jvmStartTime("jvmStartTime", true); + + @Getter + private final String key; + @Getter + private final boolean isNumberValue; + @Getter + @Nullable + private DeviationType deviationType; + + InventoryItem(String key, boolean isNumberValue) { + this.key = key; + this.isNumberValue = isNumberValue; + } + + InventoryItem(String key, boolean isNumberValue, DeviationType deviationType) { + this(key, isNumberValue); + this.deviationType = deviationType; + } + + @Nullable + public Double getDeviation(Map averageValues, @Nullable String value) { + if (averageValues.containsKey(this) && value != null) { + double averageValue = averageValues.get(this); + return getDeviation(value, averageValue); + } + return null; + } + + @Nullable + public Double getDeviation(@Nullable String value, double average) { + if (deviationType != null && value != null && average != 0 && isNumberValue) { + return Double.parseDouble(value) / average; + } + return null; + } + + public DeviationSeverity getDeviationSeverity(Double deviation, + Collection> collection, + @Nullable String value, + String currentBlockHeight) { + if (deviationType == null || deviation == null || value == null) { + return DeviationSeverity.OK; + } + + if (deviationType instanceof DeviationByPercentage) { + return ((DeviationByPercentage) deviationType).getDeviationSeverity(deviation); + } else if (deviationType instanceof DeviationByIntegerDiff) { + return ((DeviationByIntegerDiff) deviationType).getDeviationSeverity(collection, value, this); + } else if (deviationType instanceof DeviationOfHashes) { + return ((DeviationOfHashes) deviationType).getDeviationSeverity(collection, value, this, currentBlockHeight); + } else { + return DeviationSeverity.OK; + } + } +} diff --git a/inventory/src/main/java/bisq/inventory/RequestInfo.java b/core/src/main/java/bisq/core/network/p2p/inventory/model/RequestInfo.java similarity index 72% rename from inventory/src/main/java/bisq/inventory/RequestInfo.java rename to core/src/main/java/bisq/core/network/p2p/inventory/model/RequestInfo.java index 754478db9db..c663dd449db 100644 --- a/inventory/src/main/java/bisq/inventory/RequestInfo.java +++ b/core/src/main/java/bisq/core/network/p2p/inventory/model/RequestInfo.java @@ -15,9 +15,7 @@ * along with Bisq. If not, see . */ -package bisq.inventory; - -import bisq.core.network.p2p.inventory.InventoryItem; +package bisq.core.network.p2p.inventory.model; import java.util.Map; @@ -41,4 +39,16 @@ public class RequestInfo { public RequestInfo(long requestStartTime) { this.requestStartTime = requestStartTime; } + + public String getDisplayValue(InventoryItem inventoryItem) { + String value = getValue(inventoryItem); + return value != null ? value : "n/a"; + } + + @Nullable + public String getValue(InventoryItem inventoryItem) { + return inventory != null && inventory.containsKey(inventoryItem) ? + inventory.get(inventoryItem) : + null; + } } diff --git a/inventory/src/main/java/bisq/inventory/InventoryMonitor.java b/inventory/src/main/java/bisq/inventory/InventoryMonitor.java index 0021d977d65..55b4565ceac 100644 --- a/inventory/src/main/java/bisq/inventory/InventoryMonitor.java +++ b/inventory/src/main/java/bisq/inventory/InventoryMonitor.java @@ -19,7 +19,9 @@ import bisq.core.network.p2p.inventory.GetInventoryRequestManager; -import bisq.core.network.p2p.inventory.InventoryItem; +import bisq.core.network.p2p.inventory.model.Average; +import bisq.core.network.p2p.inventory.model.InventoryItem; +import bisq.core.network.p2p.inventory.model.RequestInfo; import bisq.core.network.p2p.seed.DefaultSeedNodeRepository; import bisq.core.proto.network.CoreNetworkProtoResolver; @@ -50,7 +52,6 @@ @Slf4j public class InventoryMonitor implements SetupListener { - private final Map jsonFileManagerByNodeAddress = new HashMap<>(); private final Map> requestInfoListByNode = new HashMap<>(); private final File appDir; @@ -58,10 +59,16 @@ public class InventoryMonitor implements SetupListener { private final int intervalSec; private final NetworkNode networkNode; private final GetInventoryRequestManager getInventoryRequestManager; + private ArrayList seedNodes; private InventoryWebServer inventoryWebServer; private int requestCounter = 0; + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + public InventoryMonitor(File appDir, boolean useLocalhostForP2P, BaseCurrencyNetwork network, @@ -87,10 +94,26 @@ public InventoryMonitor(File appDir, }); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void shutDown(Runnable shutDownCompleteHandler) { + networkNode.shutDown(shutDownCompleteHandler); + jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown); + inventoryWebServer.shutDown(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // SetupListener + /////////////////////////////////////////////////////////////////////////////////////////// + @Override public void onTorNodeReady() { - UserThread.runPeriodically(this::requestAllSeeds, intervalSec); - requestAllSeeds(); + UserThread.runPeriodically(this::requestFromAllSeeds, intervalSec); + requestFromAllSeeds(); } @Override @@ -105,13 +128,12 @@ public void onSetupFailed(Throwable throwable) { public void onRequestCustomBridges() { } - public void shutDown(Runnable shutDownCompleteHandler) { - networkNode.shutDown(shutDownCompleteHandler); - jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown); - inventoryWebServer.shutDown(); - } - private void requestAllSeeds() { + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void requestFromAllSeeds() { requestCounter++; seedNodes.forEach(nodeAddress -> { RequestInfo requestInfo = new RequestInfo(System.currentTimeMillis()); @@ -121,7 +143,6 @@ private void requestAllSeeds() { result -> processResponse(nodeAddress, requestInfo, result, null), errorMessage -> processResponse(nodeAddress, requestInfo, null, errorMessage)); }).start(); - }); } @@ -147,11 +168,11 @@ private void processResponse(NodeAddress nodeAddress, // We create average of all nodes latest results. It might be that the nodes last result is // from a previous request as the response has not arrived yet. - Set requestInfoSetOfOtherNodes = requestInfoListByNode.values().stream() + Set requestInfoSet = requestInfoListByNode.values().stream() .filter(list -> !list.isEmpty()) .map(list -> list.get(list.size() - 1)) .collect(Collectors.toSet()); - Map averageValues = InventoryUtil.getAverageValues(requestInfoSetOfOtherNodes); + Map averageValues = Average.of(requestInfoSet); inventoryWebServer.onNewRequestInfo(requestInfoListByNode, averageValues, requestCounter); diff --git a/inventory/src/main/java/bisq/inventory/InventoryUtil.java b/inventory/src/main/java/bisq/inventory/InventoryUtil.java deleted file mode 100644 index 4d3392f300b..00000000000 --- a/inventory/src/main/java/bisq/inventory/InventoryUtil.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 . - */ - -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.Objects; -import java.util.Set; - -import static com.google.common.base.Preconditions.checkArgument; - -public class InventoryUtil { - - public static Map getAverageValues(Set requestInfoSetOfOtherNodes) { - Map 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 requestInfoSetOfOtherNodes, - InventoryItem inventoryItem) { - checkArgument(inventoryItem.getType().equals(Integer.class)); - return requestInfoSetOfOtherNodes.stream() - .map(RequestInfo::getInventory) - .filter(Objects::nonNull) - .filter(inventory -> inventory.containsKey(inventoryItem)) - .mapToInt(inventory -> Integer.parseInt(inventory.get(inventoryItem))) - .average() - .orElse(0d); - } - - public static double getAverageFromLongValues(Set requestInfoSetOfOtherNodes, - InventoryItem inventoryItem) { - checkArgument(inventoryItem.getType().equals(Long.class)); - return requestInfoSetOfOtherNodes.stream() - .map(RequestInfo::getInventory) - .filter(Objects::nonNull) - .filter(inventory -> inventory.containsKey(inventoryItem)) - .mapToLong(inventory -> Long.parseLong(inventory.get(inventoryItem))) - .average() - .orElse(0d); - } - - public static double getAverageFromDoubleValues(Set requestInfoSetOfOtherNodes, - InventoryItem inventoryItem) { - checkArgument(inventoryItem.getType().equals(Double.class)); - return requestInfoSetOfOtherNodes.stream() - .map(RequestInfo::getInventory) - .filter(Objects::nonNull) - .filter(inventory -> inventory.containsKey(inventoryItem)) - .mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem)))) - .average() - .orElse(0d); - } - - - public static DeviationSeverity getDeviationSeverityByIntegerDistance(Map> map, - RequestInfo requestInfo, - InventoryItem inventoryItem, - int warnTrigger, - int alertTrigger) { - DeviationSeverity deviationSeverity = DeviationSeverity.OK; - Map sameItemsByValue = 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(Objects::nonNull) - .map(e -> e.get(inventoryItem)) - .forEach(e -> { - sameItemsByValue.putIfAbsent(e, 0); - int prev = sameItemsByValue.get(e); - sameItemsByValue.put(e, prev + 1); - }); - if (sameItemsByValue.size() > 1) { - List> sameItems = new ArrayList<>(); - sameItemsByValue.forEach((key, value) -> sameItems.add(new Tuple2<>(key, value))); - sameItems.sort(Comparator.comparing(o -> o.second)); - Collections.reverse(sameItems); - String majority = sameItems.get(0).first; - Map inventory = requestInfo.getInventory(); - if (inventory != null) { - String candidate = inventory.get(inventoryItem); - if (!majority.equals(candidate)) { - int majorityAsInt = Integer.parseInt(majority); - int candidateAsInt = Integer.parseInt(candidate); - int diff = Math.abs(majorityAsInt - candidateAsInt); - if (diff >= alertTrigger) { - deviationSeverity = DeviationSeverity.ALERT; - } else if (diff >= warnTrigger) { - deviationSeverity = DeviationSeverity.WARN; - } else { - deviationSeverity = DeviationSeverity.OK; - } - } - } - } - return deviationSeverity; - } - - public static DeviationSeverity getDeviationSeverityForHash(Map> map, - String daoStateChainHeightAsString, - RequestInfo requestInfo, - InventoryItem inventoryItem) { - DeviationSeverity deviationSeverity = DeviationSeverity.OK; - Map sameHashesPerHashListByHash = 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(Objects::nonNull) - .filter(inventory -> inventory.get(InventoryItem.daoStateChainHeight).equals(daoStateChainHeightAsString)) - .map(inventory -> inventory.get(inventoryItem)) - .forEach(value -> { - sameHashesPerHashListByHash.putIfAbsent(value, 0); - int prev = sameHashesPerHashListByHash.get(value); - sameHashesPerHashListByHash.put(value, prev + 1); - }); - if (sameHashesPerHashListByHash.size() > 1) { - List> sameHashesPerHashList = new ArrayList<>(); - sameHashesPerHashListByHash.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. - Map inventory = requestInfo.getInventory(); - if (inventory != null) { - if (sameHashesPerHashList.get(0).first.equals(inventory.get(inventoryItem))) { - // 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; - } -} diff --git a/inventory/src/main/java/bisq/inventory/InventoryWebServer.java b/inventory/src/main/java/bisq/inventory/InventoryWebServer.java index bbc8e2d8f54..fec0a020de2 100644 --- a/inventory/src/main/java/bisq/inventory/InventoryWebServer.java +++ b/inventory/src/main/java/bisq/inventory/InventoryWebServer.java @@ -17,8 +17,9 @@ package bisq.inventory; -import bisq.core.network.p2p.inventory.DeviationSeverity; -import bisq.core.network.p2p.inventory.InventoryItem; +import bisq.core.network.p2p.inventory.model.DeviationSeverity; +import bisq.core.network.p2p.inventory.model.InventoryItem; +import bisq.core.network.p2p.inventory.model.RequestInfo; import bisq.core.util.FormattingUtils; import bisq.network.p2p.NodeAddress; @@ -28,6 +29,7 @@ import java.io.BufferedReader; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -48,9 +50,15 @@ public class InventoryWebServer { private final List seedNodes; private final Map operatorByNodeAddress = new HashMap<>(); + private String html; private int requestCounter; + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + public InventoryWebServer(int port, List seedNodes, BufferedReader seedNodeFile) { @@ -64,6 +72,11 @@ public InventoryWebServer(int port, }); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + public void onNewRequestInfo(Map> requestInfoListByNode, Map averageValues, int requestCounter) { @@ -75,6 +88,11 @@ public void shutDown() { Spark.stop(); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // HTML + /////////////////////////////////////////////////////////////////////////////////////////// + private String generateHtml(Map> map, Map averageValues) { StringBuilder html = new StringBuilder(); @@ -97,13 +115,13 @@ private String generateHtml(Map> map, List list = map.get(seedNode); int numResponses = list.size(); RequestInfo requestInfo = list.get(numResponses - 1); - html.append("").append(getSeedNodeInfo(seedNode, requestInfo, averageValues)).append("") + html.append("").append(getSeedNodeInfo(seedNode, requestInfo)).append("") .append("").append(getRequestInfo(requestInfo, numResponses)).append("") .append("").append(getDataInfo(requestInfo, averageValues, map)).append("") .append("").append(getDaoInfo(requestInfo, averageValues, map)).append("") - .append("").append(getNetworkInfo(requestInfo, averageValues)).append(""); + .append("").append(getNetworkInfo(requestInfo, averageValues, map)).append(""); } else { - html.append("").append(getSeedNodeInfo(seedNode, null, averageValues)).append("") + html.append("").append(getSeedNodeInfo(seedNode, null)).append("") .append("").append("n/a").append("") .append("").append("n/a").append("") .append("").append("n/a").append("") @@ -116,9 +134,13 @@ private String generateHtml(Map> map, return html.toString(); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Sub sections + /////////////////////////////////////////////////////////////////////////////////////////// + private String getSeedNodeInfo(NodeAddress nodeAddress, - @Nullable RequestInfo requestInfo, - Map averageValues) { + @Nullable RequestInfo requestInfo) { StringBuilder sb = new StringBuilder(); String operator = operatorByNodeAddress.get(nodeAddress.getFullAddress()); @@ -128,19 +150,21 @@ private String getSeedNodeInfo(NodeAddress nodeAddress, sb.append("Node address: ").append(address).append("
"); if (requestInfo != null) { - addInventoryItem("Version: ", requestInfo, sb, InventoryItem.version); - addInventoryItem("Memory used: ", requestInfo, averageValues, sb, InventoryItem.usedMemory, - value -> Utilities.readableFileSize(Long.parseLong(value))); - String jvmStartTimeString = addInventoryItem("Node started at: ", - requestInfo, - null, - sb, - InventoryItem.jvmStartTime, - value -> new Date(Long.parseLong(value)).toString()); - - String duration = jvmStartTimeString != null ? - FormattingUtils.formatDurationAsWords( - System.currentTimeMillis() - Long.parseLong(jvmStartTimeString), + sb.append("Version: ").append(requestInfo.getDisplayValue(InventoryItem.version)).append("
"); + String memory = requestInfo.getValue(InventoryItem.usedMemory); + String memoryString = memory != null ? Utilities.readableFileSize(Long.parseLong(memory)) : "n/a"; + sb.append("Memory used: ") + .append(memoryString) + .append("
"); + + String jvmStartTimeString = requestInfo.getValue(InventoryItem.jvmStartTime); + long jvmStartTime = jvmStartTimeString != null ? Long.parseLong(jvmStartTimeString) : 0; + sb.append("Node started at: ") + .append(new Date(jvmStartTime).toString()) + .append("
"); + + String duration = jvmStartTime > 0 ? + FormattingUtils.formatDurationAsWords(System.currentTimeMillis() - jvmStartTime, true, true) : "n/a"; sb.append("Run duration: ").append(duration).append("
"); @@ -160,20 +184,20 @@ private String getRequestInfo(RequestInfo requestInfo, int numResponses) { sb.append("Number of responses: ").append(getColorTagByDeviationSeverity(deviationSeverity)) .append(numResponses).append(CLOSE_TAG); + DeviationSeverity rrtDeviationSeverity = DeviationSeverity.OK; + String rrtString = "n/a"; if (requestInfo.getResponseTime() > 0) { long rrt = requestInfo.getResponseTime() - requestInfo.getRequestStartTime(); - DeviationSeverity rrtDeviationSeverity = DeviationSeverity.OK; if (rrt > 20_000) { rrtDeviationSeverity = DeviationSeverity.ALERT; } else if (rrt > 10_000) { rrtDeviationSeverity = DeviationSeverity.WARN; } - String rrtString = MathUtils.roundDouble(rrt / 1000d, 3) + " sec"; - sb.append("Round trip time: ").append(getColorTagByDeviationSeverity(rrtDeviationSeverity)) - .append(rrtString).append(CLOSE_TAG); - } else { - sb.append("Round trip time: ").append("n/a").append(CLOSE_TAG); + rrtString = MathUtils.roundDouble(rrt / 1000d, 3) + " sec"; + } + sb.append("Round trip time: ").append(getColorTagByDeviationSeverity(rrtDeviationSeverity)) + .append(rrtString).append(CLOSE_TAG); Date requestStartTime = new Date(requestInfo.getRequestStartTime()); sb.append("Requested at: ").append(requestStartTime).append("
"); @@ -185,7 +209,7 @@ private String getRequestInfo(RequestInfo requestInfo, int numResponses) { String errorMessage = requestInfo.getErrorMessage(); if (errorMessage != null && !errorMessage.isEmpty()) { - sb.append("Error message: ").append(getColorTagByDeviationSeverity(DeviationSeverity.WARN)) + sb.append("Error message: ").append(getColorTagByDeviationSeverity(DeviationSeverity.ALERT)) .append(errorMessage).append(CLOSE_TAG); } return sb.toString(); @@ -195,33 +219,18 @@ private String getDataInfo(RequestInfo requestInfo, Map averageValues, Map> map) { StringBuilder sb = new StringBuilder(); - addInventoryItem(requestInfo, averageValues, sb, InventoryItem.OfferPayload); - addInventoryItem(requestInfo, averageValues, sb, InventoryItem.MailboxStoragePayload); - addInventoryItem(requestInfo, averageValues, sb, InventoryItem.TradeStatistics3); - - DeviationSeverity deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.Alert, 1, 1); - addInventoryItem(getTitle(InventoryItem.Alert), requestInfo, averageValues, sb, InventoryItem.Alert, - null, deviationSeverity); - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.Filter, 1, 1); - addInventoryItem(getTitle(InventoryItem.Filter), requestInfo, averageValues, sb, InventoryItem.Filter, - null, deviationSeverity); + sb.append(getLine(InventoryItem.OfferPayload, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.MailboxStoragePayload, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.TradeStatistics3, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.AccountAgeWitness, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.SignedWitness, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.Alert, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.Filter, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.Mediator, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.RefundAgent, requestInfo, averageValues, map.values())); - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.Mediator, 1, 1); - addInventoryItem(getTitle(InventoryItem.Mediator), requestInfo, averageValues, sb, InventoryItem.Mediator, - null, deviationSeverity); - - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.RefundAgent, 1, 1); - addInventoryItem(getTitle(InventoryItem.RefundAgent), requestInfo, averageValues, sb, InventoryItem.RefundAgent, - null, deviationSeverity); - - addInventoryItem(requestInfo, averageValues, sb, InventoryItem.AccountAgeWitness); - addInventoryItem(requestInfo, averageValues, sb, InventoryItem.SignedWitness); return sb.toString(); } @@ -229,183 +238,133 @@ private String getDaoInfo(RequestInfo requestInfo, Map averageValues, Map> map) { StringBuilder sb = new StringBuilder(); - DeviationSeverity deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.numBsqBlocks, 1, 3); - addInventoryItem("Number of BSQ blocks: ", requestInfo, averageValues, sb, InventoryItem.numBsqBlocks, - null, deviationSeverity); - - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.TempProposalPayload, 3, 5); - addInventoryItem(getTitle(InventoryItem.TempProposalPayload), requestInfo, averageValues, sb, InventoryItem.TempProposalPayload, - null, deviationSeverity); - - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.ProposalPayload, 1, 2); - addInventoryItem(getTitle(InventoryItem.ProposalPayload), requestInfo, averageValues, sb, InventoryItem.ProposalPayload, - null, deviationSeverity); - - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.BlindVotePayload, 1, 2); - addInventoryItem(getTitle(InventoryItem.BlindVotePayload), requestInfo, averageValues, sb, InventoryItem.BlindVotePayload, - null, deviationSeverity); - - deviationSeverity = InventoryUtil.getDeviationSeverityByIntegerDistance(map, - requestInfo, InventoryItem.daoStateChainHeight, 1, 3); - String daoStateChainHeightAsString = addInventoryItem("DAO state block height: ", requestInfo, - averageValues, sb, InventoryItem.daoStateChainHeight, null, deviationSeverity); - - DeviationSeverity daoStateHashDeviationSeverity = InventoryUtil.getDeviationSeverityForHash(map, - daoStateChainHeightAsString, - requestInfo, - InventoryItem.daoStateHash); - addInventoryItem("DAO state hash: ", requestInfo, null, sb, - InventoryItem.daoStateHash, null, daoStateHashDeviationSeverity); + + sb.append(getLine("Number of BSQ blocks: ", InventoryItem.numBsqBlocks, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.TempProposalPayload, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.ProposalPayload, requestInfo, averageValues, map.values())); + sb.append(getLine(InventoryItem.BlindVotePayload, requestInfo, averageValues, map.values())); + sb.append(getLine("DAO state block height: ", InventoryItem.daoStateChainHeight, requestInfo, averageValues, map.values())); + + String daoStateChainHeight = null; + if (requestInfo.getInventory() != null && requestInfo.getInventory().containsKey(InventoryItem.daoStateChainHeight)) { + daoStateChainHeight = requestInfo.getInventory().get(InventoryItem.daoStateChainHeight); + } + + sb.append(getLine("DAO state hash: ", InventoryItem.daoStateHash, requestInfo, averageValues, map.values(), daoStateChainHeight)); // 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); + sb.append(getLine("Proposal state hash: ", InventoryItem.proposalHash, requestInfo, averageValues, map.values(), daoStateChainHeight)); + sb.append(getLine("Blind vote state hash: ", InventoryItem.blindVoteHash, requestInfo, averageValues, map.values(), daoStateChainHeight)); return sb.toString(); } private String getNetworkInfo(RequestInfo requestInfo, - Map averageValues) { + Map averageValues, + Map> map) { StringBuilder sb = new StringBuilder(); - addInventoryItem("Max. connections: ", requestInfo, averageValues, sb, InventoryItem.maxConnections); - addInventoryItem("Number of connections: ", requestInfo, averageValues, sb, InventoryItem.numConnections); - addInventoryItem("Peak number of connections: ", requestInfo, averageValues, sb, InventoryItem.peakNumConnections); - addInventoryItem("Number of 'All connections lost' events: ", requestInfo, averageValues, sb, InventoryItem.numAllConnectionsLostEvents); - addInventoryItem("Sent messages/sec: ", requestInfo, averageValues, sb, InventoryItem.sentMessagesPerSec, - value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2))); - addInventoryItem("Received messages/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedMessagesPerSec, - value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2))); - addInventoryItem("Sent kB/sec: ", requestInfo, averageValues, sb, InventoryItem.sentBytesPerSec, - value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value) / 1024, 2))); - addInventoryItem("Received kB/sec: ", requestInfo, averageValues, sb, InventoryItem.receivedBytesPerSec, - value -> String.valueOf(MathUtils.roundDouble(Double.parseDouble(value) / 1024, 2))); - addInventoryItem("Sent data: ", requestInfo, averageValues, sb, InventoryItem.sentBytes, - value -> Utilities.readableFileSize(Long.parseLong(value))); - addInventoryItem("Received data: ", requestInfo, averageValues, sb, InventoryItem.receivedBytes, - value -> Utilities.readableFileSize(Long.parseLong(value))); + + sb.append(getLine("Max. connections: ", InventoryItem.maxConnections, requestInfo, averageValues, map.values())); + sb.append(getLine("Number of connections: ", InventoryItem.numConnections, requestInfo, averageValues, map.values())); + sb.append(getLine("Peak number of connections: ", InventoryItem.peakNumConnections, requestInfo, averageValues, map.values())); + sb.append(getLine("Number of 'All connections lost' events: ", InventoryItem.numAllConnectionsLostEvents, requestInfo, averageValues, map.values())); + + sb.append(getLine("Sent messages/sec: ", InventoryItem.sentMessagesPerSec, requestInfo, + averageValues, map.values(), null, this::getRounded)); + sb.append(getLine("Received messages/sec: ", InventoryItem.receivedMessagesPerSec, requestInfo, + averageValues, map.values(), null, this::getRounded)); + sb.append(getLine("Sent kB/sec: ", InventoryItem.sentBytesPerSec, requestInfo, + averageValues, map.values(), null, this::getRounded)); + sb.append(getLine("Received kB/sec: ", InventoryItem.receivedBytesPerSec, requestInfo, + averageValues, map.values(), null, this::getRounded)); + sb.append(getLine("Sent data: ", InventoryItem.sentBytes, requestInfo, + averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value)))); + sb.append(getLine("Received data: ", InventoryItem.receivedBytes, requestInfo, + averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value)))); return sb.toString(); } - private String addInventoryItem(RequestInfo requestInfo, - Map averageValues, - StringBuilder sb, - InventoryItem inventoryItem) { - return addInventoryItem(getTitle(inventoryItem), - requestInfo, - averageValues, - sb, - inventoryItem); - } - private String addInventoryItem(String title, - RequestInfo requestInfo, - StringBuilder sb, - InventoryItem inventoryItem) { - return addInventoryItem(title, + /////////////////////////////////////////////////////////////////////////////////////////// + // Utils + /////////////////////////////////////////////////////////////////////////////////////////// + + private String getLine(InventoryItem inventoryItem, + RequestInfo requestInfo, + Map averageValues, + Collection> collection) { + return getLine(getTitle(inventoryItem), + inventoryItem, requestInfo, - null, - sb, - inventoryItem); + averageValues, + collection); } - private String addInventoryItem(String title, - RequestInfo requestInfo, - @Nullable Map averageValues, - StringBuilder sb, - InventoryItem inventoryItem) { - return addInventoryItem(title, + private String getLine(String title, + InventoryItem inventoryItem, + RequestInfo requestInfo, + Map averageValues, + Collection> collection) { + return getLine(title, + inventoryItem, requestInfo, averageValues, - sb, - inventoryItem, + collection, null, null); } - private String addInventoryItem(String title, - RequestInfo requestInfo, - @Nullable Map averageValues, - StringBuilder sb, - InventoryItem inventoryItem, - @Nullable Function formatter) { - return addInventoryItem(title, + private String getLine(String title, + InventoryItem inventoryItem, + RequestInfo requestInfo, + Map averageValues, + Collection> collection, + @Nullable String daoStateChainHeight) { + return getLine(title, + inventoryItem, requestInfo, averageValues, - sb, - inventoryItem, - formatter, + collection, + daoStateChainHeight, null); } - private String addInventoryItem(String title, - RequestInfo requestInfo, - @Nullable Map averageValues, - StringBuilder sb, - InventoryItem inventoryItem, - @Nullable Function formatter, - @Nullable DeviationSeverity deviationSeverity) { - String valueAsString = null; - String displayString = "n/a"; - String deviationAsString = ""; - String colorTag = getColorTagByDeviationSeverity(DeviationSeverity.OK); - Map inventory = requestInfo.getInventory(); - if (inventory != null && inventory.containsKey(inventoryItem)) { - valueAsString = inventory.get(inventoryItem); - if (averageValues != null && averageValues.containsKey(inventoryItem)) { - double average = averageValues.get(inventoryItem); - double deviation = 0; - if (inventoryItem.getType().equals(Integer.class)) { - int value = Integer.parseInt(valueAsString); - deviation = value / average; - } else if (inventoryItem.getType().equals(Long.class)) { - long value = Long.parseLong(valueAsString); - deviation = value / average; - } else if (inventoryItem.getType().equals(Double.class)) { - double value = Double.parseDouble(valueAsString); - deviation = value / average; - } - - if (!inventoryItem.getType().equals(String.class)) { - colorTag = getColorTagByDeviationSeverity(inventoryItem.getDeviationSeverity(deviation)); - deviationAsString = " (" + MathUtils.roundDouble(100 * deviation, 2) + " %)"; - } - } - - if (deviationSeverity != null) { - colorTag = getColorTagByDeviationSeverity(deviationSeverity); - } + private String getLine(String title, + InventoryItem inventoryItem, + RequestInfo requestInfo, + Map averageValues, + Collection> collection, + @Nullable String daoStateChainHeight, + @Nullable Function formatter) { + String displayValue = requestInfo.getDisplayValue(inventoryItem); + String value = requestInfo.getValue(inventoryItem); + if (formatter != null && value != null) { + displayValue = formatter.apply(value); + } + Double deviation = inventoryItem.getDeviation(averageValues, value); + DeviationSeverity deviationSeverity = inventoryItem.getDeviationSeverity(deviation, collection, value, daoStateChainHeight); + return title + + getColorTagByDeviationSeverity(deviationSeverity) + + displayValue + + getDeviationAsPercentString(deviation) + + CLOSE_TAG; + } - // We only do formatting if we have any value - if (formatter != null) { - displayString = formatter.apply(valueAsString); - } else { - displayString = valueAsString; - } + private String getDeviationAsPercentString(@Nullable Double deviation) { + if (deviation == null) { + return ""; } - sb.append(title).append(colorTag).append(displayString).append(deviationAsString).append(CLOSE_TAG); - return valueAsString; + return " (" + MathUtils.roundDouble(100 * deviation, 2) + " %)"; } - private String getColorTagByDeviationSeverity(DeviationSeverity deviationSeverity) { + private String getColorTagByDeviationSeverity(@Nullable DeviationSeverity deviationSeverity) { + if (deviationSeverity == null) { + return ""; + } + switch (deviationSeverity) { case WARN: return ""; @@ -417,6 +376,14 @@ private String getColorTagByDeviationSeverity(DeviationSeverity deviationSeverit } } + private String getTitle(InventoryItem inventoryItem) { + return "Number of " + inventoryItem.getKey() + ": "; + } + + private String getRounded(String value) { + return String.valueOf(MathUtils.roundDouble(Double.parseDouble(value), 2)); + } + private void setupOperatorMap(BufferedReader seedNodeFile) { seedNodeFile.lines().forEach(line -> { if (!line.startsWith("#")) { @@ -427,8 +394,4 @@ private void setupOperatorMap(BufferedReader seedNodeFile) { } }); } - - private String getTitle(InventoryItem inventoryItem) { - return "Number of " + inventoryItem.getKey() + ": "; - } } diff --git a/seednode/src/main/java/bisq/seednode/SeedNode.java b/seednode/src/main/java/bisq/seednode/SeedNode.java index 675f44dd413..ff2f2609d3d 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNode.java +++ b/seednode/src/main/java/bisq/seednode/SeedNode.java @@ -41,9 +41,6 @@ public void startApplication() { appSetup.start(); getInventoryRequestHandler = injector.getInstance(GetInventoryRequestHandler.class); - - // TODO dev test key - getInventoryRequestHandler.addPermittedRequestersPubKey("035243719433b154a86329fd23a8389bee6369698be809f7e27f003f5b5aa9eb42"); } public void shutDown() {