Add inventory module

Simple monitor dumping json files with data result and request/response time to disk. Can be used by a simple web server to show state of seed nodes.
This commit is contained in:
chimp1984 2020-10-14 02:29:45 -05:00
parent 55b693e295
commit 3521619e03
No known key found for this signature in database
GPG key ID: 9801B4EC591F90E3
5 changed files with 322 additions and 0 deletions

View file

@ -99,6 +99,7 @@ configure([project(':cli'),
project(':seednode'),
project(':statsnode'),
project(':pricenode'),
project(':inventory'),
project(':apitest')]) {
apply plugin: 'application'
@ -594,6 +595,18 @@ configure(project(':daemon')) {
}
}
configure(project(':inventory')) {
mainClassName = 'bisq.inventory.InventoryMonitorMain'
dependencies {
compile project(':core')
compile "com.google.guava:guava:$guavaVersion"
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
}
}
configure(project(':apitest')) {
mainClassName = 'bisq.apitest.ApiTestMain'

View file

@ -0,0 +1,186 @@
/*
* 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.seed.DefaultSeedNodeRepository;
import bisq.core.proto.network.CoreNetworkProtoResolver;
import bisq.network.p2p.NetworkNodeProvider;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.inventory.GetInventoryRequestManager;
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;
import java.time.Clock;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
@Slf4j
public class InventoryMonitor {
private final NetworkNode networkNode;
private final GetInventoryRequestManager getInventoryRequestManager;
private final List<NodeAddress> seedNodes = new ArrayList<>();
private final Map<NodeAddress, JsonFileManager> jsonFileManagerByNodeAddress = new HashMap<>();
private final boolean useLocalhostForP2P;
private final int intervalSec;
public InventoryMonitor(File appDir,
boolean useLocalhostForP2P,
BaseCurrencyNetwork network,
int intervalSec) {
this.useLocalhostForP2P = useLocalhostForP2P;
this.intervalSec = intervalSec;
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
);
networkNode = new NetworkNodeProvider(new CoreNetworkProtoResolver(Clock.systemDefaultZone()),
ArrayList::new,
useLocalhostForP2P,
9999,
new File(appDir, "tor"),
null,
"",
-1,
"",
null,
false,
false).get();
getInventoryRequestManager = new GetInventoryRequestManager(networkNode);
seedNodes.addAll(DefaultSeedNodeRepository.getSeedNodeAddressesFromPropertyFile(network));
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.start(new SetupListener() {
@Override
public void onTorNodeReady() {
startRequests();
}
@Override
public void onHiddenServicePublished() {
}
@Override
public void onSetupFailed(Throwable throwable) {
}
@Override
public void onRequestCustomBridges() {
}
});
}
@NotNull
private String getShortAddress(NodeAddress nodeAddress, boolean useLocalhostForP2P) {
return useLocalhostForP2P ?
nodeAddress.getFullAddress().replace(":", "_") :
nodeAddress.getFullAddress().substring(0, 10);
}
private void startRequests() {
UserThread.runPeriodically(this::requestAllSeeds, intervalSec);
requestAllSeeds();
}
private void requestAllSeeds() {
seedNodes.forEach(nodeAddress -> {
RequestInfo requestInfo = new RequestInfo(System.currentTimeMillis());
new Thread(() -> {
Thread.currentThread().setName("request @ " + getShortAddress(nodeAddress, useLocalhostForP2P));
getInventoryRequestManager.request(nodeAddress,
result -> {
log.info("nodeAddress={}, result={}", nodeAddress, result.toString());
long responseTime = System.currentTimeMillis();
requestInfo.setResponseTime(responseTime);
requestInfo.setResult(result);
String json = Utilities.objectToJson(requestInfo);
jsonFileManagerByNodeAddress.get(nodeAddress).writeToDisc(json, String.valueOf(responseTime));
},
errorMessage -> {
log.warn(errorMessage);
requestInfo.setErrorMessage(errorMessage);
});
}).start();
});
}
public void shutDown() {
jsonFileManagerByNodeAddress.values().forEach(JsonFileManager::shutDown);
}
@Getter
static class RequestInfo {
private final long requestStartTime;
@Setter
private long responseTime;
@Setter
private Map<String, Integer> result;
@Setter
private String errorMessage;
public RequestInfo(long requestStartTime) {
this.requestStartTime = requestStartTime;
}
}
}

View file

@ -0,0 +1,106 @@
/*
* 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.common.UserThread;
import bisq.common.app.Log;
import bisq.common.app.Version;
import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.util.Utilities;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.nio.file.Paths;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import ch.qos.logback.classic.Level;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Signal;
@Slf4j
public class InventoryMonitorMain {
private static InventoryMonitor inventoryMonitor;
private static boolean stopped;
// prog args for regtest: 10 1 BTC_REGTEST
public static void main(String[] args) {
int intervalSec = 600;
boolean useLocalhostForP2P = false;
BaseCurrencyNetwork network = BaseCurrencyNetwork.BTC_MAINNET;
if (args.length > 0) {
intervalSec = Integer.parseInt(args[0]);
}
if (args.length > 1) {
useLocalhostForP2P = args[1].equals("1");
}
if (args.length > 2) {
network = BaseCurrencyNetwork.valueOf(args[2]);
}
String appName = "bisq-InventoryMonitor-" + network;
File appDir = new File(Utilities.getUserDataDir(), appName);
String logPath = Paths.get(appDir.getPath(), "bisq").toString();
Log.setup(logPath);
Log.setLevel(Level.INFO);
Version.setBaseCryptoNetworkId(network.ordinal());
inventoryMonitor = new InventoryMonitor(appDir, useLocalhostForP2P, network, intervalSec);
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(inventoryMonitor.getClass().getSimpleName())
.setDaemon(true)
.build();
UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
Signal.handle(new Signal("INT"), signal -> {
shutDown();
});
Signal.handle(new Signal("TERM"), signal -> {
shutDown();
});
keepRunning();
}
private static void shutDown() {
inventoryMonitor.shutDown();
stopped = true;
System.exit(0);
}
private static void keepRunning() {
while (!stopped) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ignore) {
}
}
}
}

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30}: %msg %xEx%n)</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="CONSOLE_APPENDER"/>
</root>
<logger name="com.neemre.btcdcli4j" level="WARN"/>
<logger name="com.neemre.btcdcli4j.core.client.ClientConfigurator" level="ERROR"/>
</configuration>

View file

@ -11,6 +11,7 @@ include 'pricenode'
include 'relay'
include 'seednode'
include 'statsnode'
include 'inventory'
include 'apitest'
rootProject.name = 'bisq'