Remove monitor module

It has been extracted to https://github.com/bisq-network/bisq-monitor

Signed-off-by: HenrikJannsen <boilingfrog@gmx.com>
This commit is contained in:
HenrikJannsen 2022-11-24 16:30:34 -05:00
parent 4ad1f4d246
commit f55ab5e565
No known key found for this signature in database
GPG Key ID: 02AA2BAE387C8307
39 changed files with 0 additions and 4122 deletions

2
.gitignore vendored
View File

@ -30,8 +30,6 @@ desktop.ini
*.class
deploy
*/releases/*
/monitor/TorHiddenServiceStartupTimeTests/*
/monitor/monitor-tor/*
.java-version
.localnet
/apitest/src/main/resources/dao-setup*

View File

@ -37,7 +37,6 @@
#
# $ ls -1 bisq-*
# bisq-desktop
# bisq-monitor
# bisq-relay
# bisq-seednode
# bisq-statsnode

View File

@ -93,7 +93,6 @@ configure(subprojects) {
configure([project(':cli'),
project(':daemon'),
project(':desktop'),
project(':monitor'),
project(':seednode'),
project(':statsnode'),
project(':apitest')]) {
@ -506,52 +505,6 @@ configure(project(':desktop')) {
}
configure(project(':monitor')) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "$javafxVersion"
modules = ['javafx.base']
}
mainClassName = 'bisq.monitor.Monitor'
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
dependencies {
implementation project(':assets')
implementation project(':common')
implementation project(':core')
implementation project(':p2p')
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
compileOnly "org.projectlombok:lombok:$lombokVersion"
implementation "ch.qos.logback:logback-classic:$logbackVersion"
implementation "ch.qos.logback:logback-core:$logbackVersion"
implementation "com.google.guava:guava:$guavaVersion"
implementation "org.slf4j:slf4j-api:$slf4jVersion"
implementation("com.github.bisq-network.netlayer:tor.external:$netlayerVersion") {
exclude(module: 'slf4j-api')
}
implementation("com.github.bisq-network.netlayer:tor.native:$netlayerVersion") {
exclude(module: 'slf4j-api')
}
implementation("com.google.inject:guice:$guiceVersion") {
exclude(module: 'guava')
}
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
}
}
configure(project(':seednode')) {
apply plugin: 'com.github.johnrengelman.shadow'

View File

@ -1,246 +0,0 @@
# Bisq Network Monitor Node
The Bisq monitor node collects a set of metrics which are of interest to developers and users alike. These metrics are then made available through reporters.
The *Settled* release features these metrics:
- Tor Startup Time: The time it takes to start Tor starting at a clean system, unpacking the shipped Tor binaries, firing up Tor until Tor is connected to the Tor network and ready to use.
- Tor Roundtrip Time: Given a bootstrapped Tor, the roundtrip time of connecting to a hidden service is measured.
- Tor Hidden Service Startup Time: Given a bootstrapped Tor, the time it takes to create and announce a freshly created hidden service.
- P2P Round Trip Time: A metric hitchhiking the Ping/Pong messages of the Keep-Alive-Mechanism to determine the Round Trip Time when issuing a Ping to a seed node.
- P2P Seed Node Message Snapshot: Get absolute number and constellation of messages a fresh Bisq client will get on startup. Also reports diffs between seed nodes on a per-message-type basis.
- P2P Network Load: listens to the P2P network and its broadcast messages. Reports every X seconds.
- P2P Market Statistics: a demonstration metric which extracts market information from broadcast messages. This demo implementation reports the number of open offers per market.
The *Settled* release features these reporters:
- A reporter that simply writes the findings to `System.err`
- A reporter that reports the findings to a Graphite/Carbon instance using the [plaintext protocol](https://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol)
## Configuration
The *Bisq Network Monitor Node* is to be configured via a Java properties file. There is a default configuration file shipped with the monitor which reports to the one monitoring service currently up and running.
If you want to tweak the configuration, you can pass the location of the file as command line parameter:
```
./bisq-monitor /path/to/your/config.properties
```
A sample configuration file looks like follows:
```
## System configuration
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
System.useConsoleReporter=true
# 0 -> BTC_MAINNET, 1 -> BTC_TESTNET (default)
System.baseCurrencyNetwork=0
## Each Metric is configured via a set of properties.
##
## The minimal set of properties required to run a Metric is:
##
## YourMetricName.enabled=true|false
## YourMetricName.run.interval=10 [seconds]
#Edit and uncomment the lines below to your liking
#TorStartupTime Metric
TorStartupTime.enabled=true
TorStartupTime.run.interval=100
TorStartupTime.run.socksPort=90500 # so that there is no interference with a system Tor
#TorRoundTripTime Metric
TorRoundTripTime.enabled=true
TorRoundTripTime.run.interval=100
TorRoundTripTime.run.sampleSize=5
TorRoundTripTime.run.hosts=http://expyuzz4wqqyqhjn.onion:80 # torproject.org hidden service
#TorHiddenServiceStartupTime Metric
TorHiddenServiceStartupTime.enabled=true
TorHiddenServiceStartupTime.run.interval=100
TorHiddenServiceStartupTime.run.localPort=90501 # so that there is no interference with a system Tor
TorHiddenServiceStartupTime.run.servicePort=90511 # so that there is no interference with a system Tor
#P2PRoundTripTime Metric
P2PRoundTripTime.enabled=true
P2PRoundTripTime.run.interval=100
P2PRoundTripTime.run.sampleSize=5
P2PRoundTripTime.run.hosts=723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000
P2PRoundTripTime.run.torProxyPort=9060
#P2PNetworkLoad Metric
P2PNetworkLoad.enabled=true
P2PNetworkLoad.run.interval=100
P2PNetworkLoad.run.torProxyPort=9061
P2PNetworkLoad.run.historySize=500
#P2PNetworkMessageSnapshot Metric
P2PSeedNodeSnapshot.enabled=true
P2PSeedNodeSnapshot.run.interval=24
P2PSeedNodeSnapshot.run.hosts=3f3cu2yw7u457ztq.onion:8000, 723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000
P2PSeedNodeSnapshot.run.torProxyPort=9062
#P2PMarketStats Metric
P2PMarketStats.enabled=true
P2PMarketStats.run.interval=37
P2PMarketStats.run.hosts=ef5qnzx6znifo3df.onion:8000
P2PMarketStats.run.torProxyPort=9063
#PriceNodeStats Metric
PriceNodeStats.enabled=true
PriceNodeStats.run.interval=42
PriceNodeStats.run.hosts=http://5bmpx76qllutpcyp.onion, http://xc3nh4juf2hshy7e.onion, http://44mgyoe2b6oqiytt.onion, http://62nvujg5iou3vu3i.onion, http://ceaanhbvluug4we6.onion
#MarketStats Metric
MarketStats.enabled=true
MarketStats.run.interval=191
## Reporters are configured via a set of properties as well.
##
## In contrast to Metrics, Reporters do not have a minimal set of properties.
#GraphiteReporter
GraphiteReporter.serviceUrl=k6evlhg44acpchtc.onion:2003
```
## Run
The distribution ships with a systemd .service file. Validate/change the executable/config paths within the shipped `bisq-monitor.service` file and copy/move the file to your systemd directory (something along `/usr/lib/systemd/system/`). Now you can control your *Monitor Node* via the usual systemd start/stop commands
```
systemctl start bisq-monitor.service
systemctl stop bisq-monitor.service
```
and
```
systemctl enable bisq-monitor.service
```
You can reload the configuration without restarting the service by using
```
systemctl reload bisq-monitor.service
```
Follow the logs created by the service by inspecting
```
journalctl --unit bisq-monitor --follow
```
# Monitoring Service
A typical monitoring service consists of a [Graphite](https://graphiteapp.org/) and a [Grafana](https://grafana.com/) instance.
Both are available via Docker-containers.
## Setting up Graphite
### Install
For a docker setup, use
```
docker run -d --name graphite --restart=always -p 2003:2003 -p 8080:8080 graphiteapp/graphite-statsd
```
- Port 2003 is used for the [plaintext protocol](https://graphite.readthedocs.io/en/latest/feeding-carbon.html#the-plaintext-protocol) mentioned above
- Port 8080 offers an API for user interfaces.
more information can be found [here](https://graphite.readthedocs.io/en/latest/install.html)
### Configuration
For configuration, you must adapt the whisper database schema to suit your needs. First, stop your docker container by running
```
docker stop graphite
```
Find your config files within the `Source` directory stated in
```
docker inspect graphite | grep -C 2 graphite/conf\",
```
Edit `storage-schemas.conf` so that the frequency of your incoming data (configured in the monitor configs `interval`) is matched. For example, insert
```
[bisq]
pattern = ^bisq.*
retentions = 10s:1h,5m:31d,30m:2y,1h:5y
```
before the `[default...` blocks of the file. This basically says, that every incoming set of data reflects 5 minutes of the time series. Furthermore, every 30 minutes, the data is compressed and thus, takes less memory as it is kept for 2 years.
Further, edit `storage-aggregation.conf` to configure how your data is compressed. For example, insert
```
[bisq]
pattern=^bisq.*
xFilesFactor = 0
aggregationMethod = average
```
before the `[default...` blocks of the file. With this configuration, whenever data is aggregated, the `average` data is made available given that at least `0%` of the data points (i.e. floor(30 / 5 * 40%) = 2 data points) exist. Otherwise, the aggregated data is dropped. Since we start the first hour with a frequency of 10s but only supply data every 4 to 6 minutes, our aggregated values would get dropped.
*Please note, that I have not been able to get the whole thing to work without the 10s:1h part yet*
Finally, update the database. For doing that, go to the storage directory of graphite, the `Source` directory stated in
```
docker inspect graphite | grep -C 2 graphite/conf\",
```
Once there, you have two options:
- delete the whisper directory
```
rm -r whisper
```
- update the database by doing
```
find ./ -type f -name '*.wsp' -exec whisper-resize.py --nobackup {} 10s:1h 5m:31d 30m:2y 1h:5y \;
```
and finally, restart your graphite container:
```
docker start graphite
```
Other than that, there is no further configuration necessary. However, you might change your iptables/firewalls to not let anyone access your Graphite instance from the outside.
### Backup your data
The metric data is kept in the `Source` directory stated in
```
docker inspect graphite | grep -C 2 graphite/conf\",
```
ready to be backed up regularly.
## Setting up Grafana
### Install
For a docker setup, use
```
docker run -d --name=grafana -p 3000:3000 grafana/grafana
```
- Port 3000 offers the web interface
more information can be found [here](https://grafana.com/grafana/download?platform=docker)
### Configuration
- Once you have Grafana up and running, go to the *Data Source* configuration tab.
- Once there click *Add data source* and select *Graphite*.
- In the HTTP section enter the IP address of your graphite docker container and the port `8080` (as we have configured before). E.g. `http://172.170.1:8080`
- Select `Server (default)` as an *Access* method and hit *Save & Test*.
You should be all set. You can now proceed to add Dashboards, Panels and finally display the prettiest Graphs you can think of.
A working connection to Graphite should let you add your data series in a *Graph*s *Metrics* tab in a pretty intuitive way.
- Optional: hide your Grafana instance behind a reverse proxy like nginx and add some TLS.
- Optional: make your Grafana instance accessible via a Tor hidden service.
### Backup your data
Grafana stores every dashboard as a JSON model. This model can be accessed (copied/restored) within the dashboard's settings and its *JSON Model* tab. Do with the data whatever you want.

View File

@ -1,16 +0,0 @@
[Unit]
Description=Bisq network monitor
After=network.target
[Service]
WorkingDirectory=~
Environment="JAVA_OPTS='-Xmx500M'"
ExecStart=/home/bisq/bisq/bisq-monitor /home/bisq/monitor.properties
ExecReload=/bin/kill -USR1 $MAINPID
Restart=on-failure
User=bisq
Group=bisq
[Install]
WantedBy=multi-user.target

View File

@ -1,136 +0,0 @@
Hostname "__ONION_ADDRESS__"
Interval 30
LoadPlugin syslog
<Plugin syslog>
LogLevel info
</Plugin>
LoadPlugin cpu
LoadPlugin df
LoadPlugin disk
LoadPlugin fhcount
LoadPlugin interface
LoadPlugin java
LoadPlugin load
LoadPlugin memory
LoadPlugin processes
LoadPlugin swap
LoadPlugin write_graphite
<Plugin cpu>
ReportByCpu true
ValuesPercentage true
</Plugin>
<Plugin df>
MountPoint "/"
</Plugin>
<Plugin disk>
Disk "/[hs]da/"
</Plugin>
<Plugin fhcount>
ValuesAbsolute false
ValuesPercentage true
</Plugin>
<Plugin interface>
Interface "eth0"
</Plugin>
<Plugin java>
JVMArg "-verbose:jni"
JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:/usr/share/collectd/java/generic-jmx.jar"
LoadPlugin "org.collectd.java.GenericJMX"
<Plugin "GenericJMX">
# Generic heap/nonheap memory usage.
<MBean "memory">
ObjectName "java.lang:type=Memory"
#InstanceFrom ""
InstancePrefix "memory"
# Creates four values: committed, init, max, used
<Value>
Type "memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
Attribute "HeapMemoryUsage"
InstancePrefix "heap-"
</Value>
# Creates four values: committed, init, max, used
<Value>
Type "memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
Attribute "NonHeapMemoryUsage"
InstancePrefix "nonheap-"
</Value>
</MBean>
# Memory usage by memory pool.
<MBean "memory_pool">
ObjectName "java.lang:type=MemoryPool,*"
InstancePrefix "memory_pool-"
InstanceFrom "name"
<Value>
Type "memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
Attribute "Usage"
</Value>
</MBean>
<Connection>
ServiceURL "service:jmx:rmi:///jndi/rmi://localhost:6969/jmxrmi"
Collect "memory_pool"
Collect "memory"
</Connection>
# See /usr/share/doc/collectd/examples/GenericJMX.conf
# for an example config.
</Plugin>
</Plugin>
#<Plugin load>
# ReportRelative true
#</Plugin>
#<Plugin memory>
# ValuesAbsolute true
# ValuesPercentage false
#</Plugin>
#<Plugin processes>
# Process "name"
# ProcessMatch "foobar" "/usr/bin/perl foobar\\.pl.*"
#</Plugin>
#<Plugin swap>
# ReportByDevice false
# ReportBytes true
#</Plugin>
<Plugin write_graphite>
<Node "node">
Host "127.0.0.1"
Port "2003"
Protocol "tcp"
ReconnectInterval 0
LogSendErrors false
Prefix "servers."
StoreRates true
AlwaysAppendDS false
EscapeCharacter "_"
SeparateInstances false
PreserveSeparator false
DropDuplicateFields false
</Node>
</Plugin>

View File

@ -1,75 +0,0 @@
#!/bin/bash
set -e
echo "[*] Bisq Server Monitoring installation script"
##### change paths if necessary for your system
BISQ_REPO_URL=https://raw.githubusercontent.com/bisq-network/bisq
BISQ_REPO_TAG=master
ROOT_USER=root
ROOT_GROUP=root
ROOT_HOME=~root
ROOT_PKG=(nginx collectd openssl)
SYSTEMD_ENV_HOME=/etc/default
#####
echo "[*] Gathering information"
read -p "Please provide the onion address of your service (eg. 3f3cu2yw7u457ztq): " onionaddress
echo "[*] Updating apt repo sources"
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get update -q
echo "[*] Upgrading OS packages"
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get upgrade -qq -y
echo "[*] Installing base packages"
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ${ROOT_PKG[@]}
echo "[*] Preparing Bisq init script for monitoring"
# remove stuff it it is there already
for file in "${SYSTEMD_ENV_HOME}/bisq.env" "${SYSTEMD_ENV_HOME}/bisq-pricenode.env"
do
if [ -f "$file" ];then
sudo -H -i -u "${ROOT_USER}" sed -i -e 's/-Dcom.sun.management.jmxremote //g' -e 's/-Dcom.sun.management.jmxremote.local.only=true//g' -e 's/ -Dcom.sun.management.jmxremote.host=127.0.0.1//g' -e 's/ -Dcom.sun.management.jmxremote.port=6969//g' -e 's/ -Dcom.sun.management.jmxremote.rmi.port=6969//g' -e 's/ -Dcom.sun.management.jmxremote.ssl=false//g' -e 's/ -Dcom.sun.management.jmxremote.authenticate=false//g' "${file}"
sudo -H -i -u "${ROOT_USER}" sed -i -e '/JAVA_OPTS/ s/"$/ -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=true -Dcom.sun.management.jmxremote.host=127.0.0.1 -Dcom.sun.management.jmxremote.port=6969 -Dcom.sun.management.jmxremote.rmi.port=6969 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"/' "${file}"
fi
done
echo "[*] Seeding entropy from /dev/urandom"
sudo -H -i -u "${ROOT_USER}" /bin/sh -c "head -1500 /dev/urandom > ${ROOT_HOME}/.rnd"
echo "[*] Installing Nginx config"
sudo -H -i -u "${ROOT_USER}" openssl req -x509 -nodes -newkey rsa:2048 -days 3000 -keyout /etc/nginx/cert.key -out /etc/nginx/cert.crt -subj="/O=Bisq/OU=Bisq Infrastructure/CN=$onionaddress"
curl -s "${BISQ_REPO_URL}/${BISQ_REPO_TAG}/monitor/nginx.conf" > /tmp/nginx.conf
sudo -H -i -u "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 /tmp/nginx.conf /etc/nginx/nginx.conf
echo "[*] Installing collectd config"
curl -s "${BISQ_REPO_URL}/${BISQ_REPO_TAG}/monitor/collectd.conf" > /tmp/collectd.conf
sudo -H -i -u "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 /tmp/collectd.conf /etc/collectd/collectd.conf
sudo -H -i -u "${ROOT_USER}" sed -i -e "s/__ONION_ADDRESS__/$onionaddress/" /etc/collectd/collectd.conf
echo "[*] Updating systemd daemon configuration"
sudo -H -i -u "${ROOT_USER}" systemctl daemon-reload
sudo -H -i -u "${ROOT_USER}" systemctl enable nginx.service
sudo -H -i -u "${ROOT_USER}" systemctl enable collectd.service
echo "[*] Restarting services"
set +e
service bisq status >/dev/null 2>&1
[ $? != 4 ] && sudo -H -i -u "${ROOT_USER}" systemctl restart bisq.service
service bisq-pricenode status >/dev/null 2>&1
[ $? != 4 ] && sudo -H -i -u "${ROOT_USER}" systemctl restart bisq-pricenode.service
sudo -H -i -u "${ROOT_USER}" systemctl restart nginx.service
sudo -H -i -u "${ROOT_USER}" systemctl restart collectd.service
echo '[*] Done!'
echo ' '
echo '[*] Report this certificate to the monitoring team!'
echo '----------------------------------------------------------------'
echo "Server: $onionaddress"
echo ' '
cat /etc/nginx/cert.crt
echo '----------------------------------------------------------------'
echo ' '

View File

@ -1,28 +0,0 @@
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
worker_processes 1;
events {
worker_connections 1024;
}
stream {
log_format basic '$remote_addr [$time_local] '
'$protocol Status $status Sent $bytes_sent Received $bytes_received '
'Time $session_time';
error_log syslog:server=unix:/dev/log;
access_log syslog:server=unix:/dev/log basic;
server {
listen 127.0.0.1:2003;
proxy_pass monitor.bisq.network:2002;
proxy_ssl on;
proxy_ssl_certificate /etc/nginx/cert.crt;
proxy_ssl_certificate_key /etc/nginx/cert.key;
proxy_ssl_session_reuse on;
}
}

View File

@ -1,52 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.network.p2p.network.TorMode;
import org.berndpruenster.netlayer.tor.Tor;
import java.io.File;
/**
* This class uses an already defined Tor via <code>Tor.getDefault()</code>
*
* @author Florian Reimair
*
*/
public class AvailableTor extends TorMode {
private final String hiddenServiceDirectory;
public AvailableTor(File torWorkingDirectory, String hiddenServiceDirectory) {
super(torWorkingDirectory);
this.hiddenServiceDirectory = hiddenServiceDirectory;
}
@Override
public Tor getTor() {
return Tor.getDefault();
}
@Override
public String getHiddenServiceDirectory() {
return hiddenServiceDirectory;
}
}

View File

@ -1,74 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import java.util.Properties;
/**
* Does some pre-computation for a configurable class.
*
* @author Florian Reimair
*/
public abstract class Configurable {
protected Properties configuration = new Properties();
private String name;
/**
* Filters all java properties starting with {@link Configurable#getName()} of
* the class and makes them available. Does <em>NOT</em> parse the content of
* the properties!
* <p>
* For example, if the implementing class sets its name (using
* {@link Configurable#setName(String)}) to <code>MyName</code>, the list of
* properties is scanned for properties starting with <code>MyName</code>.
* Matching lines are made available to the class without the prefix. For
* example, a property <code>MyName.answer=42</code> is made available as
* <code>configuration.getProperty("answer")</code> resulting in
* <code>42</code>.
*
* @param properties a set of configuration properties
*/
public void configure(final Properties properties) {
// only configure the Properties which belong to us
final Properties myProperties = new Properties();
properties.forEach((k, v) -> {
String key = (String) k;
if (key.startsWith(getName()))
myProperties.put(key.substring(key.indexOf(".") + 1), v);
});
// configure all properties that belong to us
this.configuration = myProperties;
}
protected String getName() {
return name;
}
/**
* Set the name used to filter through configuration properties. See
* {@link Configurable#configure(Properties)}.
*
* @param name the name of the configurable
*/
protected void setName(String name) {
this.name = name;
}
}

View File

@ -1,147 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.common.app.Version;
import bisq.common.util.Utilities;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import static bisq.common.config.Config.BASE_CURRENCY_NETWORK;
/**
* Starts a Metric (in its own {@link Thread}), manages its properties and shuts
* it down gracefully. Furthermore, configuration updates and execution are done
* in a thread-save manner. Implementing classes only have to implement the
* {@link Metric#execute()} method.
*
* @author Florian Reimair
*/
@Slf4j
public abstract class Metric extends Configurable implements Runnable {
private static final String INTERVAL = "run.interval";
private static ScheduledExecutorService executor;
protected final Reporter reporter;
private ScheduledFuture<?> scheduler;
/**
* disable execution
*/
private void disable() {
if (scheduler != null)
scheduler.cancel(false);
}
/**
* enable execution
*/
private void enable() {
scheduler = executor.scheduleWithFixedDelay(this, new Random().nextInt(60),
Long.parseLong(configuration.getProperty(INTERVAL)), TimeUnit.SECONDS);
}
/**
* Constructor.
*/
protected Metric(Reporter reporter) {
this.reporter = reporter;
setName(this.getClass().getSimpleName());
if (executor == null) {
executor = new ScheduledThreadPoolExecutor(6);
}
}
boolean enabled() {
if (scheduler != null)
return !scheduler.isCancelled();
else
return false;
}
@Override
public void configure(final Properties properties) {
synchronized (this) {
log.info("{} (re)loading config...", getName());
super.configure(properties);
reporter.configure(properties);
Version.setBaseCryptoNetworkId(Integer.parseInt(properties.getProperty("System." + BASE_CURRENCY_NETWORK, "1"))); // defaults to BTC_TESTNET
// decide whether to enable or disable the task
if (configuration.isEmpty() || !configuration.getProperty("enabled", "false").equals("true")
|| !configuration.containsKey(INTERVAL)) {
disable();
// some informative log output
if (configuration.isEmpty())
log.error("{} is not configured at all. Will not run.", getName());
else if (!configuration.getProperty("enabled", "false").equals("true"))
log.info("{} is deactivated. Will not run.", getName());
else if (!configuration.containsKey(INTERVAL))
log.error("{} is missing mandatory '" + INTERVAL + "' property. Will not run.", getName());
else
log.error("{} is mis-configured. Will not run.", getName());
} else if (!enabled() && configuration.getProperty("enabled", "false").equals("true")) {
// check if this Metric got activated after being disabled.
// if so, resume execution
enable();
log.info("{} got activated. Starting up.", getName());
}
}
}
@Override
public void run() {
try {
Thread.currentThread().setName("Metric: " + getName());
// execute all the things
synchronized (this) {
log.info("{} started", getName());
execute();
log.info("{} done", getName());
}
} catch (Throwable e) {
log.error("A metric misbehaved!", e);
}
}
/**
* Gets scheduled repeatedly.
*/
protected abstract void execute();
/**
* initiate an orderly shutdown on all metrics. Blocks until all metrics are
* shut down or after one minute.
*/
public static void haltAllMetrics() {
Utilities.shutdownAndAwaitTermination(executor, 2, TimeUnit.MINUTES);
}
}

View File

@ -1,182 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.MarketStats;
import bisq.monitor.metric.P2PMarketStats;
import bisq.monitor.metric.P2PNetworkLoad;
import bisq.monitor.metric.P2PRoundTripTime;
import bisq.monitor.metric.P2PSeedNodeSnapshot;
import bisq.monitor.metric.PriceNodeStats;
import bisq.monitor.metric.TorHiddenServiceStartupTime;
import bisq.monitor.metric.TorRoundTripTime;
import bisq.monitor.metric.TorStartupTime;
import bisq.monitor.reporter.ConsoleReporter;
import bisq.monitor.reporter.GraphiteReporter;
import bisq.common.app.Capabilities;
import bisq.common.app.Capability;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
import sun.misc.Signal;
/**
* Monitor executable for the Bisq network.
*
* @author Florian Reimair
*/
@Slf4j
public class Monitor {
public static final File TOR_WORKING_DIR = new File("monitor/work/monitor-tor");
private static String[] args = {};
public static void main(String[] args) throws Throwable {
Monitor.args = args;
new Monitor().start();
}
/**
* A list of all active {@link Metric}s
*/
private final List<Metric> metrics = new ArrayList<>();
/**
* Starts up all configured Metrics.
*
* @throws Throwable in case something goes wrong
*/
private void start() throws Throwable {
// start Tor
Tor.setDefault(new NativeTor(TOR_WORKING_DIR, null, null, false));
//noinspection deprecation,deprecation,deprecation,deprecation,deprecation,deprecation,deprecation,deprecation
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.REFUND_AGENT,
Capability.MEDIATION,
Capability.TRADE_STATISTICS_3);
// assemble Metrics
// - create reporters
Reporter graphiteReporter = new GraphiteReporter();
// only use ConsoleReporter if requested (for debugging for example)
Properties properties = getProperties();
if ("true".equals(properties.getProperty("System.useConsoleReporter", "false")))
graphiteReporter = new ConsoleReporter();
// - add available metrics with their reporters
metrics.add(new TorStartupTime(graphiteReporter));
metrics.add(new TorRoundTripTime(graphiteReporter));
metrics.add(new TorHiddenServiceStartupTime(graphiteReporter));
metrics.add(new P2PRoundTripTime(graphiteReporter));
metrics.add(new P2PNetworkLoad(graphiteReporter));
metrics.add(new P2PSeedNodeSnapshot(graphiteReporter));
metrics.add(new P2PMarketStats(graphiteReporter));
metrics.add(new PriceNodeStats(graphiteReporter));
metrics.add(new MarketStats(graphiteReporter));
// prepare configuration reload
// Note that this is most likely only work on Linux
Signal.handle(new Signal("USR1"), signal -> {
try {
configure();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
// configure Metrics
// - which also starts the metrics if appropriate
configure();
// exit Metrics gracefully on shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// set the name of the Thread for debugging purposes
log.info("system shutdown initiated");
log.info("shutting down active metrics...");
Metric.haltAllMetrics();
try {
log.info("shutting down tor...");
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
} catch (Throwable ignore) {
}
log.info("system halt");
}, "Monitor Shutdown Hook ")
);
}
/**
* Reload the configuration from disk.
*
* @throws Exception if something goes wrong
*/
private void configure() throws Exception {
Properties properties = getProperties();
for (Metric current : metrics)
current.configure(properties);
}
/**
* Overloads a default set of properties with a file if given
*
* @return a set of properties
* @throws Exception in case something goes wrong
*/
private Properties getProperties() throws Exception {
Properties result = new Properties();
// if we have a config file load the config file, else, load the default config
// from the resources
if (args.length > 0)
result.load(new FileInputStream(args[0]));
else
result.load(Monitor.class.getClassLoader().getResourceAsStream("metrics.properties"));
return result;
}
}

View File

@ -1,47 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.network.p2p.NodeAddress;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Helper for parsing and pretty printing onion addresses.
*
* @author Florian Reimair
*/
public class OnionParser {
public static NodeAddress getNodeAddress(final String current) throws MalformedURLException {
String nodeAddress = current.trim();
if (!nodeAddress.startsWith("http://"))
nodeAddress = "http://" + nodeAddress;
URL tmp = new URL(nodeAddress);
return new NodeAddress(tmp.getHost(), tmp.getPort() > 0 ? tmp.getPort() : 80);
}
public static String prettyPrint(final NodeAddress host) {
return host.getHostNameWithoutPostFix();
}
public static String prettyPrint(String host) throws MalformedURLException {
return prettyPrint(getNodeAddress(host));
}
}

View File

@ -1,74 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import java.util.Map;
/**
* Reports findings to a specific service/file/place using the proper means to
* do so.
*
* @author Florian Reimair
*/
public abstract class Reporter extends Configurable {
protected Reporter() {
setName(this.getClass().getSimpleName());
}
/**
* Report our findings.
*
* @param value the value to report
*/
public abstract void report(long value);
/**
* Report our findings
*
* @param value the value to report
* @param prefix a common prefix to be included in the tag name
*/
public abstract void report(long value, String prefix);
/**
* Report our findings.
*
* @param values Map<metric name, metric value>
*/
public abstract void report(Map<String, String> values);
/**
* Report our findings.
*
* @param values Map<metric name, metric value>
* @param prefix for example "torStartupTime"
*/
public abstract void report(Map<String, String> values, String prefix);
/**
* Report our findings one by one.
*
* @param key the metric name
* @param value the value to report
* @param timestamp a unix timestamp in milliseconds
* @param prefix for example "torStartupTime"
*/
public abstract void report(String key, String value, String timestamp, String prefix);
}

View File

@ -1,70 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
/**
* Calculates average, max, min, p25, p50, p75 off of a list of samples and
* throws in the sample size for good measure.
*
* @author Florian Reimair
*/
public class StatisticsHelper {
public static Map<String, String> process(Collection<Long> input) {
List<Long> samples = new ArrayList<>(input);
// aftermath
Collections.sort(samples);
// - average, max, min , sample size
LongSummaryStatistics statistics = samples.stream().mapToLong(val -> val).summaryStatistics();
Map<String, String> results = new HashMap<>();
results.put("average", String.valueOf(Math.round(statistics.getAverage())));
results.put("max", String.valueOf(statistics.getMax()));
results.put("min", String.valueOf(statistics.getMin()));
results.put("sampleSize", String.valueOf(statistics.getCount()));
// - p25, median, p75
Integer[] percentiles = new Integer[] { 25, 50, 75 };
for (Integer percentile : percentiles) {
double rank = statistics.getCount() * percentile / 100.0;
Long percentileValue;
if (samples.size() <= rank + 1)
percentileValue = samples.get(samples.size() - 1);
else if (Math.floor(rank) == rank)
percentileValue = samples.get((int) rank);
else
percentileValue = Math.round(samples.get((int) Math.floor(rank))
+ (samples.get((int) (Math.floor(rank) + 1)) - samples.get((int) Math.floor(rank)))
/ (rank - Math.floor(rank)));
results.put("p" + percentile, String.valueOf(percentileValue));
}
return results;
}
}

View File

@ -1,81 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* Gate pattern to help with thread synchronization
*
* @author Florian Reimair
*/
@Slf4j
public class ThreadGate {
private CountDownLatch lock = new CountDownLatch(0);
/**
* Make everyone wait until the gate is open again.
*/
public void engage() {
lock = new CountDownLatch(1);
}
/**
* Make everyone wait until the gate is open again.
*
* @param numberOfLocks how often the gate has to be unlocked until the gate
* opens.
*/
public void engage(int numberOfLocks) {
lock = new CountDownLatch(numberOfLocks);
}
/**
* Wait for the gate to be opened. Blocks until the gate is open again. Returns
* immediately if the gate is already open.
*/
public synchronized void await() {
while (lock.getCount() > 0)
try {
if (!lock.await(60, TimeUnit.SECONDS)) {
log.warn("timeout occurred!");
break; // break the loop
}
} catch (InterruptedException ignore) {
}
}
/**
* Open the gate and let everyone proceed with their execution.
*/
public void proceed() {
lock.countDown();
}
/**
* Open the gate with no regards on how many locks are still in place.
*/
public void unlock() {
while (lock.getCount() > 0)
lock.countDown();
}
}

View File

@ -1,100 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Metric;
import bisq.monitor.Reporter;
import java.net.URL;
import java.net.URLConnection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
/**
* Uses the markets API to retrieve market volume data.
*
* @author Florian Reimair
*
*/
@Slf4j
public class MarketStats extends Metric {
private static final String MARKETS_BISQ_NETWORK = "https://markets.bisq.network";
// poor mans JSON parser
private final Pattern marketPattern = Pattern.compile("\"market\" ?: ?\"([a-z_]+)\"");
private final Pattern amountPattern = Pattern.compile("\"amount\" ?: ?\"([\\d\\.]+)\"");
private final Pattern volumePattern = Pattern.compile("\"volume\" ?: ?\"([\\d\\.]+)\"");
private final Pattern timestampPattern = Pattern.compile("\"trade_date\" ?: ?([\\d]+)");
private Long lastRun = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(15));
public MarketStats(Reporter reporter) {
super(reporter);
}
@Override
protected void execute() {
try {
// for each configured host
Map<String, String> result = new HashMap<>();
// assemble query
long now = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
String query = "/api/trades?format=json&market=all&timestamp_from=" + lastRun + "&timestamp_to=" + now;
lastRun = now; // thought about adding 1 second but what if a trade is done exactly in this one second?
// connect
URLConnection connection = new URL(MARKETS_BISQ_NETWORK + query).openConnection();
// prepare to receive data
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line, all = "";
while ((line = in.readLine()) != null)
all += ' ' + line;
in.close();
Arrays.stream(all.substring(0, all.length() - 2).split("}")).forEach(trade -> {
Matcher market = marketPattern.matcher(trade);
Matcher amount = amountPattern.matcher(trade);
Matcher timestamp = timestampPattern.matcher(trade);
market.find();
if (market.group(1).endsWith("btc")) {
amount = volumePattern.matcher(trade);
}
amount.find();
timestamp.find();
reporter.report("volume." + market.group(1), amount.group(1), timestamp.group(1), getName());
});
} catch (IllegalStateException ignore) {
// no match found
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,282 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Reporter;
import bisq.core.offer.OfferUtil;
import bisq.core.offer.bisq_v1.OfferPayload;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
import bisq.common.proto.network.NetworkEnvelope;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Demo Stats metric derived from the OfferPayload messages we get from the seed nodes
*
* @author Florian Reimair
*/
@Slf4j
public class P2PMarketStats extends P2PSeedNodeSnapshotBase {
final Map<NodeAddress, Statistics<Aggregator>> versionBucketsPerHost = new ConcurrentHashMap<>();
final Map<NodeAddress, Statistics<Aggregator>> offerVolumeBucketsPerHost = new ConcurrentHashMap<>();
final Map<NodeAddress, Statistics<List<Long>>> offerVolumeDistributionBucketsPerHost = new ConcurrentHashMap<>();
final Map<NodeAddress, Statistics<Map<NodeAddress, Aggregator>>> offersPerTraderBucketsPerHost = new ConcurrentHashMap<>();
final Map<NodeAddress, Statistics<Map<NodeAddress, Aggregator>>> volumePerTraderBucketsPerHost = new ConcurrentHashMap<>();
/**
* Efficient way to aggregate numbers.
*/
private static class Aggregator {
private long value = 0;
synchronized long value() {
return value;
}
synchronized void increment() {
value++;
}
synchronized void add(long amount) {
value += amount;
}
}
private abstract static class OfferStatistics<T> extends Statistics<T> {
@Override
public synchronized void log(Object message) {
if (message instanceof OfferPayload) {
OfferPayload currentMessage = (OfferPayload) message;
// For logging different data types
String market = currentMessage.getDirection() + "." + currentMessage.getBaseCurrencyCode() + "_" + currentMessage.getCounterCurrencyCode();
process(market, currentMessage);
}
}
abstract void process(String market, OfferPayload currentMessage);
}
private class OfferCountStatistics extends OfferStatistics<Aggregator> {
@Override
void process(String market, OfferPayload currentMessage) {
buckets.putIfAbsent(market, new Aggregator());
buckets.get(market).increment();
}
}
private class OfferVolumeStatistics extends OfferStatistics<Aggregator> {
@Override
void process(String market, OfferPayload currentMessage) {
buckets.putIfAbsent(market, new Aggregator());
buckets.get(market).add(currentMessage.getAmount());
}
}
private class OfferVolumeDistributionStatistics extends OfferStatistics<List<Long>> {
@Override
void process(String market, OfferPayload currentMessage) {
buckets.putIfAbsent(market, new ArrayList<>());
buckets.get(market).add(currentMessage.getAmount());
}
}
private class OffersPerTraderStatistics extends OfferStatistics<Map<NodeAddress, Aggregator>> {
@Override
void process(String market, OfferPayload currentMessage) {
buckets.putIfAbsent(market, new HashMap<>());
buckets.get(market).putIfAbsent(currentMessage.getOwnerNodeAddress(), new Aggregator());
buckets.get(market).get(currentMessage.getOwnerNodeAddress()).increment();
}
}
private class VolumePerTraderStatistics extends OfferStatistics<Map<NodeAddress, Aggregator>> {
@Override
void process(String market, OfferPayload currentMessage) {
buckets.putIfAbsent(market, new HashMap<>());
buckets.get(market).putIfAbsent(currentMessage.getOwnerNodeAddress(), new Aggregator());
buckets.get(market).get(currentMessage.getOwnerNodeAddress()).add(currentMessage.getAmount());
}
}
private class VersionsStatistics extends Statistics<Aggregator> {
@Override
public void log(Object message) {
if (message instanceof OfferPayload) {
OfferPayload offerPayload = (OfferPayload) message;
String version = "v" + OfferUtil.getVersionFromId(offerPayload.getId());
buckets.putIfAbsent(version, new Aggregator());
buckets.get(version).increment();
}
}
}
public P2PMarketStats(Reporter graphiteReporter) {
super(graphiteReporter);
}
@Override
protected List<NetworkEnvelope> getRequests() {
List<NetworkEnvelope> result = new ArrayList<>();
Random random = new Random();
result.add(new PreliminaryGetDataRequest(random.nextInt(), hashes));
return result;
}
protected void createHistogram(List<Long> input, String market, Map<String, String> report) {
int numberOfBins = 5;
// - get biggest offer
double max = input.stream().max(Long::compareTo).map(value -> value * 1.01).orElse(0.0);
// - create histogram
input.stream().collect(
Collectors.groupingBy(aLong -> aLong == max ? numberOfBins - 1 : (int) Math.floor(aLong / (max / numberOfBins)), Collectors.counting())).
forEach((integer, integer2) -> report.put(market + ".bin_" + integer, String.valueOf(integer2)));
report.put(market + ".number_of_bins", String.valueOf(numberOfBins));
report.put(market + ".max", String.valueOf((int) max));
}
@Override
protected void report() {
Map<String, String> report = new HashMap<>();
bucketsPerHost.values().stream().findFirst().ifPresent(nodeAddressStatisticsEntry -> nodeAddressStatisticsEntry.values().forEach((market, numberOfOffers) -> report.put(market, String.valueOf(((Aggregator) numberOfOffers).value()))));
reporter.report(report, getName() + ".offerCount");
// do offerbook volume statistics
report.clear();
offerVolumeBucketsPerHost.values().stream().findFirst().ifPresent(aggregatorStatistics -> aggregatorStatistics.values().forEach((market, numberOfOffers) -> report.put(market, String.valueOf(numberOfOffers.value()))));
reporter.report(report, getName() + ".volume");
// do the offer vs volume histogram
report.clear();
// - get a data set
offerVolumeDistributionBucketsPerHost.values().stream().findFirst().ifPresent(listStatistics -> listStatistics.values().forEach((market, offers) -> {
createHistogram(offers, market, report);
}));
reporter.report(report, getName() + ".volume-per-offer-distribution");
// do offers per trader
report.clear();
// - get a data set
offersPerTraderBucketsPerHost.values().stream().findFirst().ifPresent(mapStatistics -> mapStatistics.values().forEach((market, stuff) -> {
List<Long> offerPerTrader = stuff.values().stream().map(Aggregator::value).collect(Collectors.toList());
createHistogram(offerPerTrader, market, report);
}));
reporter.report(report, getName() + ".traders_by_number_of_offers");
// do volume per trader
report.clear();
// - get a data set
volumePerTraderBucketsPerHost.values().stream().findFirst().ifPresent(mapStatistics -> mapStatistics.values().forEach((market, stuff) -> {
List<Long> volumePerTrader = stuff.values().stream().map(Aggregator::value).collect(Collectors.toList());
createHistogram(volumePerTrader, market, report);
}));
reporter.report(report, getName() + ".traders_by_volume");
// do version statistics
report.clear();
Optional<Statistics<Aggregator>> optionalStatistics = versionBucketsPerHost.values().stream().findAny();
optionalStatistics.ifPresent(aggregatorStatistics -> aggregatorStatistics.values()
.forEach((version, numberOfOccurrences) -> report.put(version, String.valueOf(numberOfOccurrences.value()))));
reporter.report(report, "versions");
}
protected boolean treatMessage(NetworkEnvelope networkEnvelope, Connection connection) {
checkNotNull(connection.getPeersNodeAddressProperty(),
"although the property is nullable, we need it to not be null");
if (networkEnvelope instanceof GetDataResponse) {
Statistics offerCount = new OfferCountStatistics();
Statistics offerVolume = new OfferVolumeStatistics();
Statistics offerVolumeDistribution = new OfferVolumeDistributionStatistics();
Statistics offersPerTrader = new OffersPerTraderStatistics();
Statistics volumePerTrader = new VolumePerTraderStatistics();
Statistics versions = new VersionsStatistics();
GetDataResponse dataResponse = (GetDataResponse) networkEnvelope;
final Set<ProtectedStorageEntry> dataSet = dataResponse.getDataSet();
dataSet.forEach(e -> {
final ProtectedStoragePayload protectedStoragePayload = e.getProtectedStoragePayload();
if (protectedStoragePayload == null) {
log.warn("StoragePayload was null: {}", networkEnvelope.toString());
return;
}
offerCount.log(protectedStoragePayload);
offerVolume.log(protectedStoragePayload);
offerVolumeDistribution.log(protectedStoragePayload);
offersPerTrader.log(protectedStoragePayload);
volumePerTrader.log(protectedStoragePayload);
versions.log(protectedStoragePayload);
});
dataResponse.getPersistableNetworkPayloadSet().forEach(persistableNetworkPayload -> {
// memorize message hashes
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
//hashes.add(bytes);
hashes.add(persistableNetworkPayload.getHash());
});
bucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), offerCount);
offerVolumeBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), offerVolume);
offerVolumeDistributionBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), offerVolumeDistribution);
offersPerTraderBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), offersPerTrader);
volumePerTraderBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), volumePerTrader);
versionBucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), versions);
return true;
}
return false;
}
}

View File

@ -1,245 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.AvailableTor;
import bisq.monitor.Metric;
import bisq.monitor.Monitor;
import bisq.monitor.Reporter;
import bisq.monitor.ThreadGate;
import bisq.core.network.p2p.seed.DefaultSeedNodeRepository;
import bisq.core.proto.network.CoreNetworkProtoResolver;
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.network.MessageListener;
import bisq.network.p2p.network.NetworkNode;
import bisq.network.p2p.network.SetupListener;
import bisq.network.p2p.network.TorNetworkNode;
import bisq.network.p2p.peers.PeerManager;
import bisq.network.p2p.peers.keepalive.KeepAliveManager;
import bisq.network.p2p.peers.peerexchange.PeerExchangeManager;
import bisq.network.p2p.storage.messages.BroadcastMessage;
import bisq.common.ClockWatcher;
import bisq.common.config.Config;
import bisq.common.file.CorruptedStorageFileHandler;
import bisq.common.persistence.PersistenceManager;
import bisq.common.proto.network.NetworkEnvelope;
import bisq.common.proto.network.NetworkProtoResolver;
import java.time.Clock;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.extern.slf4j.Slf4j;
/**
* Contacts a list of hosts and asks them for all the data we do not have. The
* answers are then compiled into buckets of message types. Based on these
* buckets, the Metric reports (for each host) the message types observed and
* their number along with a relative comparison between all hosts.
*
* @author Florian Reimair
*
*/
@Slf4j
public class P2PNetworkLoad extends Metric implements MessageListener, SetupListener {
private static final String TOR_PROXY_PORT = "run.torProxyPort";
private static final String MAX_CONNECTIONS = "run.maxConnections";
private static final String HISTORY_SIZE = "run.historySize";
private NetworkNode networkNode;
private final File torHiddenServiceDir = new File("metric_" + getName());
private final ThreadGate hsReady = new ThreadGate();
private final Map<String, Counter> buckets = new ConcurrentHashMap<>();
/**
* Buffers the last X message we received. New messages will only be logged in case
* the message isn't already in the history. Note that the oldest message hashes are
* dropped to record newer hashes.
*/
private Map<Integer, Object> history;
private long lastRun = 0;
/**
* History implementation using a {@link LinkedHashMap} and its
* {@link LinkedHashMap#removeEldestEntry(Map.Entry)} option.
*/
private static class FixedSizeHistoryTracker<K, V> extends LinkedHashMap<K, V> {
final int historySize;
FixedSizeHistoryTracker(int historySize) {
super(historySize, 10, true);
this.historySize = historySize;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > historySize;
}
}
@Override
protected void execute() {
// in case we do not have a NetworkNode up and running, we create one
if (null == networkNode) {
// prepare the gate
hsReady.engage();
// start the network node
networkNode = new TorNetworkNode(Integer.parseInt(configuration.getProperty(TOR_PROXY_PORT, "9053")),
new CoreNetworkProtoResolver(Clock.systemDefaultZone()), false,
new AvailableTor(Monitor.TOR_WORKING_DIR, torHiddenServiceDir.getName()), null);
networkNode.start(this);
// wait for the HS to be published
hsReady.await();
// boot up P2P node
try {
Config config = new Config();
CorruptedStorageFileHandler corruptedStorageFileHandler = new CorruptedStorageFileHandler();
int maxConnections = Integer.parseInt(configuration.getProperty(MAX_CONNECTIONS, "12"));
NetworkProtoResolver networkProtoResolver = new CoreNetworkProtoResolver(Clock.systemDefaultZone());
CorePersistenceProtoResolver persistenceProtoResolver = new CorePersistenceProtoResolver(null,
networkProtoResolver);
DefaultSeedNodeRepository seedNodeRepository = new DefaultSeedNodeRepository(config);
PeerManager peerManager = new PeerManager(networkNode, seedNodeRepository, new ClockWatcher(),
new PersistenceManager<>(torHiddenServiceDir, persistenceProtoResolver, corruptedStorageFileHandler), maxConnections);
// init file storage
peerManager.readPersisted(() -> {
});
PeerExchangeManager peerExchangeManager = new PeerExchangeManager(networkNode, seedNodeRepository,
peerManager);
// updates the peer list every now and then as well
peerExchangeManager
.requestReportedPeersFromSeedNodes(seedNodeRepository.getSeedNodeAddresses().iterator().next());
KeepAliveManager keepAliveManager = new KeepAliveManager(networkNode, peerManager);
keepAliveManager.start();
networkNode.addMessageListener(this);
} catch (Throwable e) {
e.printStackTrace();
}
}
// report
Map<String, String> report = new HashMap<>();
if (lastRun != 0 && System.currentTimeMillis() - lastRun != 0) {
// - normalize to data/minute
double perMinuteFactor = 60000.0 / (System.currentTimeMillis() - lastRun);
// - get snapshot so we do not loose data
Set<String> keys = new HashSet<>(buckets.keySet());
// - transfer values to report
keys.forEach(key -> {
int value = buckets.get(key).getAndReset();
if (value != 0) {
report.put(key, String.format("%.2f", value * perMinuteFactor));
}
});
// - report
reporter.report(report, getName());
}
// - reset last run
lastRun = System.currentTimeMillis();
}
public P2PNetworkLoad(Reporter reporter) {
super(reporter);
}
@Override
public void configure(Properties properties) {
super.configure(properties);
history = Collections.synchronizedMap(new FixedSizeHistoryTracker<>(Integer.parseInt(configuration.getProperty(HISTORY_SIZE, "200"))));
}
/**
* Efficient way to count message occurrences.
*/
private static class Counter {
private int value = 1;
synchronized int getAndReset() {
try {
return value;
} finally {
value = 0;
}
}
synchronized void increment() {
value++;
}
}
@Override
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (networkEnvelope instanceof BroadcastMessage) {
try {
if (history.get(networkEnvelope.hashCode()) == null) {
history.put(networkEnvelope.hashCode(), null);
buckets.get(networkEnvelope.getClass().getSimpleName()).increment();
}
} catch (NullPointerException e) {
// use exception handling because we hardly ever need to add a fresh bucket
buckets.put(networkEnvelope.getClass().getSimpleName(), new Counter());
}
}
}
@Override
public void onTorNodeReady() {
}
@Override
public void onHiddenServicePublished() {
// open the gate
hsReady.proceed();
}
@Override
public void onSetupFailed(Throwable throwable) {
}
@Override
public void onRequestCustomBridges() {
}
}

View File

@ -1,111 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.monitor.StatisticsHelper;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.CloseConnectionReason;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.peers.keepalive.messages.Ping;
import bisq.network.p2p.peers.keepalive.messages.Pong;
import bisq.common.proto.network.NetworkEnvelope;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import static com.google.common.base.Preconditions.checkNotNull;
public class P2PRoundTripTime extends P2PSeedNodeSnapshotBase {
private static final String SAMPLE_SIZE = "run.sampleSize";
private final Map<Integer, Long> sentAt = new HashMap<>();
private Map<NodeAddress, Statistics> measurements = new HashMap<>();
public P2PRoundTripTime(Reporter reporter) {
super(reporter);
}
/**
* Use a counter to do statistics.
*/
private class Statistics {
private final List<Long> samples = new ArrayList<>();
public synchronized void log(Object message) {
Pong pong = (Pong) message;
Long start = sentAt.get(pong.getRequestNonce());
if (start != null)
samples.add(System.currentTimeMillis() - start);
}
public List<Long> values() {
return samples;
}
}
@Override
protected List<NetworkEnvelope> getRequests() {
List<NetworkEnvelope> result = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < Integer.parseInt(configuration.getProperty(SAMPLE_SIZE, "1")); i++)
result.add(new Ping(random.nextInt(), 42));
return result;
}
@Override
protected void aboutToSend(NetworkEnvelope message) {
sentAt.put(((Ping) message).getNonce(), System.currentTimeMillis());
}
@Override
protected boolean treatMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (networkEnvelope instanceof Pong) {
checkNotNull(connection.getPeersNodeAddressProperty(),
"although the property is nullable, we need it to not be null");
measurements.putIfAbsent(connection.getPeersNodeAddressProperty().getValue(), new Statistics());
measurements.get(connection.getPeersNodeAddressProperty().getValue()).log(networkEnvelope);
connection.shutDown(CloseConnectionReason.APP_SHUT_DOWN);
return true;
}
return false;
}
@Override
void report() {
// report
measurements.forEach(((nodeAddress, samples) ->
reporter.report(StatisticsHelper.process(samples.values()),
getName() + "." + OnionParser.prettyPrint(nodeAddress))
));
// clean up for next round
measurements = new HashMap<>();
}
}

View File

@ -1,292 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.core.dao.monitoring.model.StateHash;
import bisq.core.dao.monitoring.network.messages.GetBlindVoteStateHashesRequest;
import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest;
import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest;
import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
import bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
import bisq.common.proto.network.NetworkEnvelope;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Contacts a list of hosts and asks them for all the data excluding persisted messages. The
* answers are then compiled into buckets of message types. Based on these
* buckets, the Metric reports (for each host) the message types observed and
* their number.
*
* Furthermore, since the DAO is a thing now, the consistency of the DAO state held by each host is assessed and reported.
*
* @author Florian Reimair
*
*/
@Slf4j
public class P2PSeedNodeSnapshot extends P2PSeedNodeSnapshotBase {
final Map<NodeAddress, Statistics<Set<Integer>>> bucketsPerHost = new ConcurrentHashMap<>();
private int daostateheight = 594000;
private int proposalheight = daostateheight;
private int blindvoteheight = daostateheight;
/**
* Use a counter to do statistics.
*/
private static class MyStatistics extends Statistics<Set<Integer>> {
@Override
public synchronized void log(Object message) {
// For logging different data types
String className = message.getClass().getSimpleName();
buckets.putIfAbsent(className, new HashSet<>());
buckets.get(className).add(message.hashCode());
}
}
public P2PSeedNodeSnapshot(Reporter reporter) {
super(reporter);
}
protected List<NetworkEnvelope> getRequests() {
List<NetworkEnvelope> result = new ArrayList<>();
Random random = new Random();
result.add(new PreliminaryGetDataRequest(random.nextInt(), hashes));
result.add(new GetDaoStateHashesRequest(daostateheight, random.nextInt()));
result.add(new GetProposalStateHashesRequest(proposalheight, random.nextInt()));
result.add(new GetBlindVoteStateHashesRequest(blindvoteheight, random.nextInt()));
return result;
}
/**
* Report all the stuff. Uses the configured reporter directly.
*/
void report() {
// report
Map<String, String> report = new HashMap<>();
// - assemble histograms
bucketsPerHost.forEach((host, statistics) -> statistics.values().forEach((type, set) -> report
.put(OnionParser.prettyPrint(host) + ".numberOfMessages." + type, Integer.toString(set.size()))));
// - assemble diffs
// - transfer values
Map<String, Statistics<Set<Integer>>> messagesPerHost = new HashMap<>();
bucketsPerHost.forEach((host, value) -> messagesPerHost.put(OnionParser.prettyPrint(host), value));
// - pick reference seed node and its values
String referenceHost = "overall_number_of_unique_messages";
Map<String, Set<Object>> referenceValues = new HashMap<>();
messagesPerHost.forEach((host, statistics) -> statistics.values().forEach((type, set) -> {
referenceValues.putIfAbsent(type, new HashSet<>());
referenceValues.get(type).addAll(set);
}));
// - calculate diffs
messagesPerHost.forEach(
(host, statistics) -> {
statistics.values().forEach((messageType, set) -> {
try {
report.put(OnionParser.prettyPrint(host) + ".relativeNumberOfMessages." + messageType,
String.valueOf(set.size() - referenceValues.get(messageType).size()));
} catch (MalformedURLException | NullPointerException e) {
log.error("we should never have gotten here", e);
}
});
try {
report.put(OnionParser.prettyPrint(host) + ".referenceHost", referenceHost);
} catch (MalformedURLException ignore) {
log.error("we should never got here");
}
});
// cleanup for next run
bucketsPerHost.forEach((host, statistics) -> statistics.reset());
// when our hash cache exceeds a hard limit, we clear the cache and start anew
if (hashes.size() > 150000)
hashes.clear();
// - report
reporter.report(report, getName());
// - assemble dao report
Map<String, String> daoreport = new HashMap<>();
// - transcode
Map<String, Map<NodeAddress, Tuple>> perType = new HashMap<>();
daoData.forEach((nodeAddress, daostatistics) -> daostatistics.values().forEach((type, tuple) -> {
perType.putIfAbsent(type, new HashMap<>());
perType.get(type).put(nodeAddress, tuple);
}));
// - process dao data
perType.forEach((type, nodeAddressTupleMap) -> {
// - find head
int head = nodeAddressTupleMap.values().stream().max(Comparator.comparingLong(Tuple::getHeight))
.map(value -> (int) value.height)
.orElse(0);
int oldest = nodeAddressTupleMap.values().stream().min(Comparator.comparingLong(Tuple::getHeight))
.map(value -> (int) value.height)
.orElse(0);
// - update queried height
if (type.contains("DaoState"))
daostateheight = oldest - 20;
else if (type.contains("Proposal"))
proposalheight = oldest - 20;
else
blindvoteheight = oldest - 20;
// - calculate diffs
nodeAddressTupleMap.forEach((nodeAddress, tuple) -> daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".head", Long.toString(tuple.height - head)));
// - memorize hashes
Map<ByteBuffer, Integer> hitcount = new HashMap<>();
nodeAddressTupleMap.forEach((nodeAddress, tuple) -> {
ByteBuffer hash = ByteBuffer.wrap(tuple.hash);
if (hitcount.containsKey(hash)) {
hitcount.put(hash, hitcount.get(hash) + 1);
} else
hitcount.put(hash, 1);
});
hitcount.clear();
nodeAddressTupleMap.forEach((nodeAddress, tuple) ->
daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".hash",
Integer.toString(Arrays.asList(hitcount.entrySet().stream()
.sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue()))
.map(Map.Entry::getKey).toArray()).indexOf(ByteBuffer
.wrap(tuple.hash)))));
// - report reference head
daoreport.put(type + ".referenceHead", Integer.toString(head));
});
daoData.clear();
// - report
reporter.report(daoreport, "DaoStateSnapshot");
}
private static class Tuple {
@Getter
private final long height;
private final byte[] hash;
Tuple(long height, byte[] hash) {
this.height = height;
this.hash = hash;
}
}
private class DaoStatistics extends Statistics<Tuple> {
@Override
public void log(Object message) {
// get last entry
StateHash last = (StateHash) ((GetStateHashesResponse) message).getStateHashes().get(((GetStateHashesResponse) message).getStateHashes().size() - 1);
// For logging different data types
String className = last.getClass().getSimpleName();
buckets.putIfAbsent(className, new Tuple(last.getHeight(), last.getHash()));
}
}
private final Map<NodeAddress, Statistics<Tuple>> daoData = new ConcurrentHashMap<>();
protected boolean treatMessage(NetworkEnvelope networkEnvelope, Connection connection) {
checkNotNull(connection.getPeersNodeAddressProperty(),
"although the property is nullable, we need it to not be null");
if (networkEnvelope instanceof GetDataResponse) {
Statistics result = new MyStatistics();
GetDataResponse dataResponse = (GetDataResponse) networkEnvelope;
final Set<ProtectedStorageEntry> dataSet = dataResponse.getDataSet();
dataSet.forEach(e -> {
final ProtectedStoragePayload protectedStoragePayload = e.getProtectedStoragePayload();
if (protectedStoragePayload == null) {
log.warn("StoragePayload was null: {}", networkEnvelope.toString());
return;
}
result.log(protectedStoragePayload);
});
dataResponse.getPersistableNetworkPayloadSet().forEach(persistableNetworkPayload -> {
// memorize message hashes
//Byte[] bytes = new Byte[persistableNetworkPayload.getHash().length];
//Arrays.setAll(bytes, n -> persistableNetworkPayload.getHash()[n]);
//hashes.add(bytes);
hashes.add(persistableNetworkPayload.getHash());
});
bucketsPerHost.put(connection.getPeersNodeAddressProperty().getValue(), result);
return true;
} else if (networkEnvelope instanceof GetStateHashesResponse) {
daoData.putIfAbsent(connection.getPeersNodeAddressProperty().getValue(), new DaoStatistics());
daoData.get(connection.getPeersNodeAddressProperty().getValue()).log(networkEnvelope);
return true;
}
return false;
}
}

View File

@ -1,241 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.AvailableTor;
import bisq.monitor.Metric;
import bisq.monitor.Monitor;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.monitor.ThreadGate;
import bisq.core.account.witness.AccountAgeWitnessStore;
import bisq.core.proto.network.CoreNetworkProtoResolver;
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
import bisq.core.trade.statistics.TradeStatistics3Store;
import bisq.network.p2p.CloseConnectionMessage;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.Connection;
import bisq.network.p2p.network.MessageListener;
import bisq.network.p2p.network.NetworkNode;
import bisq.network.p2p.network.TorNetworkNode;
import bisq.common.app.Version;
import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.persistence.PersistenceManager;
import bisq.common.proto.network.NetworkEnvelope;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
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;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
/**
* Contacts a list of hosts and asks them for all the data excluding persisted messages. The
* answers are then compiled into buckets of message types. Based on these
* buckets, the Metric reports (for each host) the message types observed and
* their number.
*
* @author Florian Reimair
*
*/
@Slf4j
public abstract class P2PSeedNodeSnapshotBase extends Metric implements MessageListener {
private static final String HOSTS = "run.hosts";
private static final String TOR_PROXY_PORT = "run.torProxyPort";
private static final String DATABASE_DIR = "run.dbDir";
final Map<NodeAddress, Statistics<?>> bucketsPerHost = new ConcurrentHashMap<>();
private final ThreadGate gate = new ThreadGate();
protected final Set<byte[]> hashes = new TreeSet<>(Arrays::compare);
/**
* Statistics Interface for use with derived classes.
*
* @param <T> the value type of the statistics implementation
*/
protected abstract static class Statistics<T> {
protected final Map<String, T> buckets = new HashMap<>();
abstract void log(Object message);
Map<String, T> values() {
return buckets;
}
void reset() {
buckets.clear();
}
}
public P2PSeedNodeSnapshotBase(Reporter reporter) {
super(reporter);
}
@Override
public void configure(Properties properties) {
super.configure(properties);
if (hashes.isEmpty() && configuration.getProperty(DATABASE_DIR) != null) {
File dir = new File(configuration.getProperty(DATABASE_DIR));
String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString();
try {
CorePersistenceProtoResolver persistenceProtoResolver = new CorePersistenceProtoResolver(null, null);
//TODO will not work with historical data... should be refactored to re-use code for reading resource files
TradeStatistics3Store tradeStatistics3Store = new TradeStatistics3Store();
PersistenceManager<TradeStatistics3Store> tradeStatistics3PersistenceManager = new PersistenceManager<>(dir,
persistenceProtoResolver, null);
tradeStatistics3PersistenceManager.initialize(tradeStatistics3Store,
tradeStatistics3Store.getDefaultStorageFileName() + networkPostfix,
PersistenceManager.Source.NETWORK);
TradeStatistics3Store persistedTradeStatistics3Store = tradeStatistics3PersistenceManager.getPersisted();
if (persistedTradeStatistics3Store != null) {
tradeStatistics3Store.getMap().putAll(persistedTradeStatistics3Store.getMap());
}
hashes.addAll(tradeStatistics3Store.getMap().keySet().stream()
.map(byteArray -> byteArray.bytes).collect(Collectors.toSet()));
AccountAgeWitnessStore accountAgeWitnessStore = new AccountAgeWitnessStore();
PersistenceManager<AccountAgeWitnessStore> accountAgeWitnessPersistenceManager = new PersistenceManager<>(dir,
persistenceProtoResolver, null);
accountAgeWitnessPersistenceManager.initialize(accountAgeWitnessStore,
accountAgeWitnessStore.getDefaultStorageFileName() + networkPostfix,
PersistenceManager.Source.NETWORK);
AccountAgeWitnessStore persistedAccountAgeWitnessStore = accountAgeWitnessPersistenceManager.getPersisted();
if (persistedAccountAgeWitnessStore != null) {
accountAgeWitnessStore.getMap().putAll(persistedAccountAgeWitnessStore.getMap());
}
hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream()
.map(byteArray -> byteArray.bytes).collect(Collectors.toSet()));
} catch (NullPointerException e) {
// in case there is no store file
log.error("There is no storage file where there should be one: {}", dir.getAbsolutePath());
}
}
}
@Override
protected void execute() {
// start the network node
final NetworkNode networkNode = new TorNetworkNode(Integer.parseInt(configuration.getProperty(TOR_PROXY_PORT, "9054")),
new CoreNetworkProtoResolver(Clock.systemDefaultZone()), false,
new AvailableTor(Monitor.TOR_WORKING_DIR, "unused"), null);
// we do not need to start the networkNode, as we do not need the HS
//networkNode.start(this);
// clear our buckets
bucketsPerHost.clear();
getRequests().forEach(getDataRequest -> send(networkNode, getDataRequest));
report();
}
protected abstract List<NetworkEnvelope> getRequests();
protected void send(NetworkNode networkNode, NetworkEnvelope message) {
ArrayList<Thread> threadList = new ArrayList<>();
// for each configured host
for (String current : configuration.getProperty(HOSTS, "").split(",")) {
threadList.add(new Thread(() -> {
try {
// parse Url
NodeAddress target = OnionParser.getNodeAddress(current);
// do the data request
aboutToSend(message);
SettableFuture<Connection> future = networkNode.sendMessage(target, message);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(Connection connection) {
connection.addMessageListener(P2PSeedNodeSnapshotBase.this);
}
@Override
public void onFailure(@NotNull Throwable throwable) {
gate.proceed();
log.error(
"Sending {} failed. That is expected if the peer is offline.\n\tException={}", message.getClass().getSimpleName(), throwable.getMessage());
}
}, MoreExecutors.directExecutor());
} catch (Exception e) {
gate.proceed(); // release the gate on error
e.printStackTrace();
}
}, current));
}
gate.engage(threadList.size());
// start all threads and wait until they all finished. We do that so we can
// minimize the time between querying the hosts and therefore the chance of
// inconsistencies.
threadList.forEach(Thread::start);
gate.await();
}
protected void aboutToSend(NetworkEnvelope message) {
}
/**
* Report all the stuff. Uses the configured reporter directly.
*/
abstract void report();
@Override
public void onMessage(NetworkEnvelope networkEnvelope, Connection connection) {
if (treatMessage(networkEnvelope, connection)) {
gate.proceed();
} else if (networkEnvelope instanceof CloseConnectionMessage) {
gate.unlock();
} else {
log.warn("Got an unexpected message of type <{}>",
networkEnvelope.getClass().getSimpleName());
}
connection.removeMessageListener(this);
}
protected abstract boolean treatMessage(NetworkEnvelope networkEnvelope, Connection connection);
}

View File

@ -1,165 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Metric;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.asset.Asset;
import bisq.asset.AssetRegistry;
import bisq.network.p2p.NodeAddress;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Fetches fee and price data from the configured price nodes.
* Based on the work of HarryMcFinned.
*
* @author Florian Reimair
* @author HarryMcFinned
*
*/
@Slf4j
public class PriceNodeStats extends Metric {
private static final String HOSTS = "run.hosts";
private static final String IGNORE = "dashTxFee ltcTxFee dogeTxFee";
// poor mans JSON parser
private final Pattern stringNumberPattern = Pattern.compile("\"(.+)\" ?: ?(\\d+)");
private final Pattern pricePattern = Pattern.compile("\"price\" ?: ?([\\d.]+)");
private final Pattern currencyCodePattern = Pattern.compile("\"currencyCode\" ?: ?\"([A-Z]+)\"");
private final List<Object> assets = Arrays.asList(new AssetRegistry().stream().map(Asset::getTickerSymbol).toArray());
public PriceNodeStats(Reporter reporter) {
super(reporter);
}
@Override
protected void execute() {
try {
// fetch proxy
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
Socks5Proxy proxy = tor.getProxy();
String[] hosts = configuration.getProperty(HOSTS, "").split(",");
Collections.shuffle(Arrays.asList(hosts));
// for each configured host
for (String current : hosts) {
Map<String, String> result = new HashMap<>();
// parse Url
NodeAddress tmp = OnionParser.getNodeAddress(current);
// connect
try {
SocksSocket socket = new SocksSocket(proxy, tmp.getHostName(), tmp.getPort());
// prepare to receive data
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// ask for fee data
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
out.println("GET /getFees/");
out.println();
out.flush();
// sift through the received lines and see if we got something json-like
String line;
while ((line = in.readLine()) != null) {
Matcher matcher = stringNumberPattern.matcher(line);
if (matcher.find())
if (!IGNORE.contains(matcher.group(1)))
result.put("fees." + matcher.group(1), matcher.group(2));
}
in.close();
out.close();
socket.close();
// connect
socket = new SocksSocket(proxy, tmp.getHostName(), tmp.getPort());
// prepare to receive data
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// ask for exchange rate data
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
out.println("GET /getAllMarketPrices/");
out.println();
out.flush();
String currencyCode = "";
while ((line = in.readLine()) != null) {
Matcher currencyCodeMatcher = currencyCodePattern.matcher(line);
Matcher priceMatcher = pricePattern.matcher(line);
if (currencyCodeMatcher.find()) {
currencyCode = currencyCodeMatcher.group(1);
if (!assets.contains(currencyCode))
currencyCode = "";
} else if (!"".equals(currencyCode) && priceMatcher.find())
result.put("price." + currencyCode, priceMatcher.group(1));
}
// close all the things
in.close();
out.close();
socket.close();
// report
reporter.report(result, getName());
// only ask for data as long as we got none
if (!result.isEmpty())
break;
} catch (IOException e) {
log.error("{} seems to be down. Trying next configured price node.", tmp.getHostName());
e.printStackTrace();
}
}
} catch (TorCtlException | IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,82 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Metric;
import bisq.monitor.Monitor;
import bisq.monitor.Reporter;
import bisq.monitor.ThreadGate;
import org.berndpruenster.netlayer.tor.HiddenServiceSocket;
import java.io.File;
import lombok.extern.slf4j.Slf4j;
/**
* A Metric to measure the startup time of a Tor Hidden Service on a already
* running Tor.
*
* @author Florian Reimair
*/
@Slf4j
public class TorHiddenServiceStartupTime extends Metric {
private static final String SERVICE_PORT = "run.servicePort";
private static final String LOCAL_PORT = "run.localPort";
private final String hiddenServiceDirectory = "metric_" + getName();
private final ThreadGate gate = new ThreadGate();
public TorHiddenServiceStartupTime(Reporter reporter) {
super(reporter);
}
@Override
protected void execute() {
// prepare settings. Fetch them every time we run the Metric so we do not have to
// restart on a config update
int localPort = Integer.parseInt(configuration.getProperty(LOCAL_PORT, "9998"));
int servicePort = Integer.parseInt(configuration.getProperty(SERVICE_PORT, "9999"));
// clear directory so we get a new onion address every time
new File(Monitor.TOR_WORKING_DIR + "/" + hiddenServiceDirectory).delete();
log.debug("creating the hidden service");
gate.engage();
// start timer - we do not need System.nanoTime as we expect our result to be in
// the range of tenth of seconds.
long start = System.currentTimeMillis();
HiddenServiceSocket hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory,
servicePort);
hiddenServiceSocket.addReadyListener(socket -> {
// stop the timer and report
reporter.report(System.currentTimeMillis() - start, getName());
log.debug("the hidden service is ready");
gate.proceed();
return null;
});
gate.await();
log.debug("going to revoke the hidden service...");
hiddenServiceSocket.close();
log.debug("[going to revoke the hidden service...] done");
}
}

View File

@ -1,92 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Metric;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.monitor.StatisticsHelper;
import bisq.network.p2p.NodeAddress;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A Metric to measure the round-trip time to the Bisq seed nodes via plain tor.
*
* @author Florian Reimair
*/
public class TorRoundTripTime extends Metric {
private static final String SAMPLE_SIZE = "run.sampleSize";
private static final String HOSTS = "run.hosts";
public TorRoundTripTime(Reporter reporter) {
super(reporter);
}
@Override
protected void execute() {
SocksSocket socket;
try {
// fetch proxy
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
Socks5Proxy proxy = tor.getProxy();
// for each configured host
for (String current : configuration.getProperty(HOSTS, "").split(",")) {
// parse Url
NodeAddress tmp = OnionParser.getNodeAddress(current);
List<Long> samples = new ArrayList<>();
while (samples.size() < Integer.parseInt(configuration.getProperty(SAMPLE_SIZE, "1"))) {
// start timer - we do not need System.nanoTime as we expect our result to be in
// seconds time.
long start = System.currentTimeMillis();
// connect
socket = new SocksSocket(proxy, tmp.getHostName(), tmp.getPort());
// by the time we get here, we are connected
samples.add(System.currentTimeMillis() - start);
// cleanup
socket.close();
}
// report
reporter.report(StatisticsHelper.process(samples), getName());
}
} catch (TorCtlException | IOException e) {
e.printStackTrace();
}
}
}

View File

@ -1,88 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.metric;
import bisq.monitor.Metric;
import bisq.monitor.Reporter;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import org.berndpruenster.netlayer.tor.Torrc;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Properties;
/**
* A Metric to measure the deployment and startup time of the packaged Tor
* binaries.
*
* @author Florian Reimair
*/
public class TorStartupTime extends Metric {
private static final String SOCKS_PORT = "run.socksPort";
private final File torWorkingDirectory = new File("monitor/work/metric_torStartupTime");
private Torrc torOverrides;
public TorStartupTime(Reporter reporter) {
super(reporter);
}
@Override
public void configure(Properties properties) {
super.configure(properties);
synchronized (this) {
LinkedHashMap<String, String> overrides = new LinkedHashMap<>();
overrides.put("SOCKSPort", configuration.getProperty(SOCKS_PORT, "90500"));
try {
torOverrides = new Torrc(overrides);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void execute() {
// cleanup installation
torWorkingDirectory.delete();
Tor tor = null;
// start timer - we do not need System.nanoTime as we expect our result to be in
// tenth of seconds time.
long start = System.currentTimeMillis();
try {
tor = new NativeTor(torWorkingDirectory, null, torOverrides);
// stop the timer and set its timestamp
reporter.report(System.currentTimeMillis() - start, getName());
} catch (TorCtlException e) {
e.printStackTrace();
} finally {
// cleanup
if (tor != null)
tor.shutdown();
}
}
}

View File

@ -1,70 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.reporter;
import bisq.monitor.Reporter;
import bisq.common.app.Version;
import bisq.common.config.BaseCurrencyNetwork;
import java.util.HashMap;
import java.util.Map;
/**
* A simple console reporter.
*
* @author Florian Reimair
*/
public class ConsoleReporter extends Reporter {
@Override
public void report(long value, String prefix) {
HashMap<String, String> result = new HashMap<>();
result.put("", String.valueOf(value));
report(result, prefix);
}
@Override
public void report(long value) {
HashMap<String, String> result = new HashMap<>();
result.put("", String.valueOf(value));
report(result);
}
@Override
public void report(Map<String, String> values, String prefix) {
String timestamp = String.valueOf(System.currentTimeMillis());
values.forEach((key, value) -> {
report(key, value, timestamp, prefix);
});
}
@Override
public void report(String key, String value, String timestamp, String prefix) {
System.err.println("Report: bisq" + (Version.getBaseCurrencyNetwork() != 0 ? "-" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].getNetwork() : "")
+ (prefix.isEmpty() ? "" : "." + prefix)
+ (key.isEmpty() ? "" : "." + key)
+ " " + value + " " + timestamp);
}
@Override
public void report(Map<String, String> values) {
report(values, "");
}
}

View File

@ -1,106 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor.reporter;
import bisq.monitor.OnionParser;
import bisq.monitor.Reporter;
import bisq.network.p2p.NodeAddress;
import bisq.common.app.Version;
import bisq.common.config.BaseCurrencyNetwork;
import org.berndpruenster.netlayer.tor.TorSocket;
import com.google.common.base.Charsets;
import java.net.Socket;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Reports our findings to a graphite service.
*
* @author Florian Reimair
*/
public class GraphiteReporter extends Reporter {
@Override
public void report(long value, String prefix) {
HashMap<String, String> result = new HashMap<>();
result.put("", String.valueOf(value));
report(result, prefix);
}
@Override
public void report(long value) {
report(value, "");
}
@Override
public void report(Map<String, String> values, String prefix) {
String timestamp = String.valueOf(System.currentTimeMillis());
values.forEach((key, value) -> {
report(key, value, timestamp, prefix);
try {
// give Tor some slack
// TODO maybe use the pickle protocol?
// https://graphite.readthedocs.io/en/latest/feeding-carbon.html
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
}
@Override
public void report(String key, String value, String timeInMilliseconds, String prefix) {
// https://graphite.readthedocs.io/en/latest/feeding-carbon.html
String report = "bisq" + (Version.getBaseCurrencyNetwork() != 0 ? "-" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].getNetwork() : "")
+ (prefix.isEmpty() ? "" : "." + prefix)
+ (key.isEmpty() ? "" : "." + key)
+ " " + value + " " + Long.parseLong(timeInMilliseconds) / 1000 + "\n";
try {
NodeAddress nodeAddress = OnionParser.getNodeAddress(configuration.getProperty("serviceUrl"));
Socket socket;
if (nodeAddress.getFullAddress().contains(".onion"))
socket = new TorSocket(nodeAddress.getHostName(), nodeAddress.getPort());
else
socket = new Socket(nodeAddress.getHostName(), nodeAddress.getPort());
socket.getOutputStream().write(report.getBytes(Charsets.UTF_8));
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void report(Map<String, String> values) {
report(values, "");
}
}

View File

@ -1,12 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.berndpruenster.netlayer.tor" level="INFO"/>
</configuration>

View File

@ -1,79 +0,0 @@
## System configuration
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
System.useConsoleReporter=true
# 0 -> BTC_MAINNET, 1 -> BTC_TESTNET (default)
System.baseCurrencyNetwork=0
## Each Metric is configured via a set of properties.
##
## The minimal set of properties required to run a Metric is:
##
## YourMetricName.enabled=true|false
## YourMetricName.run.interval=10 [seconds]
#Edit and uncomment the lines below to your liking
#TorStartupTime Metric
TorStartupTime.enabled=false
TorStartupTime.run.interval=100
TorStartupTime.run.socksPort=90500
TorRoundTripTime.enabled=false
TorRoundTripTime.run.interval=100
TorRoundTripTime.run.sampleSize=3
# torproject.org hidden service
TorRoundTripTime.run.hosts=http://expyuzz4wqqyqhjn.onion:80
#TorHiddenServiceStartupTime Metric
TorHiddenServiceStartupTime.enabled=false
TorHiddenServiceStartupTime.run.interval=100
TorHiddenServiceStartupTime.run.localPort=90501
TorHiddenServiceStartupTime.run.servicePort=90511
#P2PRoundTripTime Metric
P2PRoundTripTime.enabled=false
P2PRoundTripTime.run.interval=100
P2PRoundTripTime.run.sampleSize=5
P2PRoundTripTime.run.hosts=723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000
P2PRoundTripTime.run.torProxyPort=9060
#P2PNetworkLoad Metric
P2PNetworkLoad.enabled=false
P2PNetworkLoad.run.interval=100
P2PNetworkLoad.run.torProxyPort=9061
P2PNetworkLoad.run.historySize=200
#P2PSeedNodeSnapshotBase Metric
P2PSeedNodeSnapshot.enabled=true
P2PSeedNodeSnapshot.run.dbDir=bisq/p2p/build/resources/main/
P2PSeedNodeSnapshot.run.interval=24
P2PSeedNodeSnapshot.run.hosts=3f3cu2yw7u457ztq.onion:8000, 723ljisnynbtdohi.onion:8000, fl3mmribyxgrv63c.onion:8000
P2PSeedNodeSnapshot.run.torProxyPort=9062
#P2PMarketStats Metric
P2PMarketStats.enabled=false
P2PMarketStats.run.interval=37
P2PMarketStats.run.dbDir=bisq/p2p/build/resources/main/
P2PMarketStats.run.hosts=ef5qnzx6znifo3df.onion:8000
P2PMarketStats.run.torProxyPort=9063
#PriceNodeStats Metric
PriceNodeStats.enabled=false
PriceNodeStats.run.interval=42
PriceNodeStats.run.hosts=http://xc3nh4juf2hshy7e.onion, http://44mgyoe2b6oqiytt.onion, http://62nvujg5iou3vu3i.onion, http://ceaanhbvluug4we6.onion, http://gztmprecgqjq64zh.onion/
#MarketStats Metric
MarketStats.enabled=false
MarketStats.run.interval=191
#Another Metric
Another.run.interval=5
## Reporters are configured via a set of properties as well.
##
## In contrast to Metrics, Reporters do not have a minimal set of properties.
#GraphiteReporter
GraphiteReporter.serviceUrl=k6evlhg44acpchtc.onion:2003

View File

@ -1,149 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.reporter.ConsoleReporter;
import java.util.HashMap;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import org.junit.Assert;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@Disabled
public class MonitorInfrastructureTests {
/**
* A dummy metric for development purposes.
*/
public class Dummy extends Metric {
public Dummy() {
super(new ConsoleReporter());
}
public boolean active() {
return enabled();
}
@Override
protected void execute() {
try {
Thread.sleep(50000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@ParameterizedTest
@ValueSource(strings = {"empty", "no interval", "typo"})
public void basicConfigurationError(String configuration) {
HashMap<String, Properties> lut = new HashMap<>();
lut.put("empty", new Properties());
Properties noInterval = new Properties();
noInterval.put("Dummy.enabled", "true");
lut.put("no interval", noInterval);
Properties typo = new Properties();
typo.put("Dummy.enabled", "true");
//noinspection SpellCheckingInspection
typo.put("Dummy.run.inteval", "1");
lut.put("typo", typo);
Dummy DUT = new Dummy();
DUT.configure(lut.get(configuration));
Assert.assertFalse(DUT.active());
}
@Test
public void basicConfigurationSuccess() throws Exception {
Properties correct = new Properties();
correct.put("Dummy.enabled", "true");
correct.put("Dummy.run.interval", "1");
Dummy DUT = new Dummy();
DUT.configure(correct);
Assert.assertTrue(DUT.active());
// graceful shutdown
Metric.haltAllMetrics();
}
@Test
public void reloadConfig() throws InterruptedException, ExecutionException {
// our dummy
Dummy DUT = new Dummy();
// a second dummy to run as well
Dummy DUT2 = new Dummy();
DUT2.setName("Dummy2");
Properties dummy2Properties = new Properties();
dummy2Properties.put("Dummy2.enabled", "true");
dummy2Properties.put("Dummy2.run.interval", "1");
DUT2.configure(dummy2Properties);
// disable
DUT.configure(new Properties());
Assert.assertFalse(DUT.active());
Assert.assertTrue(DUT2.active());
// enable
Properties properties = new Properties();
properties.put("Dummy.enabled", "true");
properties.put("Dummy.run.interval", "1");
DUT.configure(properties);
Assert.assertTrue(DUT.active());
Assert.assertTrue(DUT2.active());
// disable again
DUT.configure(new Properties());
Assert.assertFalse(DUT.active());
Assert.assertTrue(DUT2.active());
// enable again
DUT.configure(properties);
Assert.assertTrue(DUT.active());
Assert.assertTrue(DUT2.active());
// graceful shutdown
Metric.haltAllMetrics();
}
@Test
public void shutdown() {
Dummy DUT = new Dummy();
DUT.setName("Dummy");
Properties dummyProperties = new Properties();
dummyProperties.put("Dummy.enabled", "true");
dummyProperties.put("Dummy.run.interval", "1");
DUT.configure(dummyProperties);
try {
Thread.sleep(2000);
Metric.haltAllMetrics();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -1,118 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.P2PNetworkLoad;
import bisq.monitor.reporter.ConsoleReporter;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Test the round trip time metric against the hidden service of tor project.org.
*
* @author Florian Reimair
*/
@Disabled
class P2PNetworkLoadTests {
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends ConsoleReporter {
private Map<String, String> results;
@Override
public void report(long value) {
Assert.fail();
}
Map<String, String> hasResults() {
return results;
}
@Override
public void report(Map<String, String> values) {
Assert.fail();
}
@Override
public void report(long value, String prefix) {
Assert.fail();
}
@Override
public void report(Map<String, String> values, String prefix) {
super.report(values, prefix);
results = values;
}
}
@BeforeAll
static void setup() throws TorCtlException {
// simulate the tor instance available to all metrics
Tor.setDefault(new NativeTor(Monitor.TOR_WORKING_DIR));
}
@Test
void run() throws Exception {
DummyReporter reporter = new DummyReporter();
// configure
Properties configuration = new Properties();
configuration.put("P2PNetworkLoad.enabled", "true");
configuration.put("P2PNetworkLoad.run.interval", "10");
configuration.put("P2PNetworkLoad.run.hosts",
"http://fl3mmribyxgrv63c.onion:8000, http://3f3cu2yw7u457ztq.onion:8000");
Metric DUT = new P2PNetworkLoad(reporter);
// start
DUT.configure(configuration);
// give it some time to start and then stop
while (!DUT.enabled())
Thread.sleep(500);
Thread.sleep(20000);
Metric.haltAllMetrics();
// observe results
Map<String, String> results = reporter.hasResults();
Assert.assertFalse(results.isEmpty());
}
@AfterAll
static void cleanup() {
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
}
}

View File

@ -1,136 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.P2PRoundTripTime;
import bisq.monitor.reporter.ConsoleReporter;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Test the round trip time metric against the hidden service of tor project.org.
*
* @author Florian Reimair
*/
@Disabled
class P2PRoundTripTimeTests {
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends ConsoleReporter {
private Map<String, String> results;
@Override
public void report(long value) {
Assert.fail();
}
Map<String, String> hasResults() {
return results;
}
@Override
public void report(Map<String, String> values) {
Assert.fail();
}
@Override
public void report(long value, String prefix) {
Assert.fail();
}
@Override
public void report(Map<String, String> values, String prefix) {
super.report(values, prefix);
results = values;
}
}
@BeforeAll
static void setup() throws TorCtlException {
// simulate the tor instance available to all metrics
Tor.setDefault(new NativeTor(Monitor.TOR_WORKING_DIR));
}
@ParameterizedTest
@ValueSource(strings = {"default", "3", "4", "10"})
void run(String sampleSize) throws Exception {
DummyReporter reporter = new DummyReporter();
// configure
Properties configuration = new Properties();
configuration.put("P2PRoundTripTime.enabled", "true");
configuration.put("P2PRoundTripTime.run.interval", "2");
if (!"default".equals(sampleSize))
configuration.put("P2PRoundTripTime.run.sampleSize", sampleSize);
// torproject.org hidden service
configuration.put("P2PRoundTripTime.run.hosts", "http://fl3mmribyxgrv63c.onion:8000");
configuration.put("P2PRoundTripTime.run.torProxyPort", "9052");
Metric DUT = new P2PRoundTripTime(reporter);
// start
DUT.configure(configuration);
// give it some time to start and then stop
while (!DUT.enabled())
Thread.sleep(2000);
Metric.haltAllMetrics();
// observe results
Map<String, String> results = reporter.hasResults();
Assert.assertFalse(results.isEmpty());
Assert.assertEquals(results.get("sampleSize"), sampleSize.equals("default") ? "1" : sampleSize);
Integer p25 = Integer.valueOf(results.get("p25"));
Integer p50 = Integer.valueOf(results.get("p50"));
Integer p75 = Integer.valueOf(results.get("p75"));
Integer min = Integer.valueOf(results.get("min"));
Integer max = Integer.valueOf(results.get("max"));
Integer average = Integer.valueOf(results.get("average"));
Assert.assertTrue(0 < min);
Assert.assertTrue(min <= p25 && p25 <= p50);
Assert.assertTrue(p50 <= p75);
Assert.assertTrue(p75 <= max);
Assert.assertTrue(min <= average && average <= max);
}
@AfterAll
static void cleanup() {
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
}
}

View File

@ -1,115 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.PriceNodeStats;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import java.io.File;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* @author Florian Reimair
*/
@Disabled
public class PriceNodeStatsTests {
private final static File torWorkingDirectory = new File("monitor/" + PriceNodeStatsTests.class.getSimpleName());
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends Reporter {
private Map<String, String> results;
@Override
public void report(long value) {
Assert.fail();
}
public Map<String, String> results() {
return results;
}
@Override
public void report(Map<String, String> values) {
results = values;
}
@Override
public void report(Map<String, String> values, String prefix) {
report(values);
}
@Override
public void report(String key, String value, String timestamp, String prefix) {
}
@Override
public void report(long value, String prefix) {
report(value);
}
}
@BeforeAll
public static void setup() throws TorCtlException {
// simulate the tor instance available to all metrics
Tor.setDefault(new NativeTor(torWorkingDirectory));
}
@Test
public void connect() {
DummyReporter reporter = new DummyReporter();
Metric DUT = new PriceNodeStats(reporter);
Properties configuration = new Properties();
configuration.put("PriceNodeStats.run.hosts", "http://5bmpx76qllutpcyp.onion");
DUT.configure(configuration);
DUT.execute();
Assert.assertNotNull(reporter.results());
Assert.assertTrue(reporter.results.size() > 0);
}
@AfterAll
public static void cleanup() {
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
torWorkingDirectory.delete();
}
}

View File

@ -1,115 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.TorHiddenServiceStartupTime;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import java.io.File;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.google.common.base.Preconditions.checkNotNull;
@Disabled // Ignore for normal test runs as the tests take lots of time
public class TorHiddenServiceStartupTimeTests {
private final static File torWorkingDirectory = new File("monitor/" + TorHiddenServiceStartupTimeTests.class.getSimpleName());
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends Reporter {
private long result;
@Override
public void report(long value) {
result = value;
}
public long results() {
return result;
}
@Override
public void report(Map<String, String> values) {
report(Long.parseLong(values.values().iterator().next()));
}
@Override
public void report(Map<String, String> values, String prefix) {
report(values);
}
@Override
public void report(String key, String value, String timestamp, String prefix) {
}
@Override
public void report(long value, String prefix) {
report(value);
}
}
@BeforeAll
public static void setup() throws TorCtlException {
// simulate the tor instance available to all metrics
Tor.setDefault(new NativeTor(torWorkingDirectory));
}
@Test
public void run() throws Exception {
DummyReporter reporter = new DummyReporter();
// configure
Properties configuration = new Properties();
configuration.put("TorHiddenServiceStartupTime.enabled", "true");
configuration.put("TorHiddenServiceStartupTime.run.interval", "5");
Metric DUT = new TorHiddenServiceStartupTime(reporter);
// start
DUT.configure(configuration);
// give it some time and then stop
Thread.sleep(180 * 1000);
Metric.haltAllMetrics();
// observe results
Assert.assertTrue(reporter.results() > 0);
}
@AfterAll
public static void cleanup() {
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
torWorkingDirectory.delete();
}
}

View File

@ -1,142 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.TorRoundTripTime;
import org.berndpruenster.netlayer.tor.NativeTor;
import org.berndpruenster.netlayer.tor.Tor;
import org.berndpruenster.netlayer.tor.TorCtlException;
import java.io.File;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Test the round trip time metric against the hidden service of tor project.org.
*
* @author Florian Reimair
*/
@Disabled // Ignore for normal test runs as the tests take lots of time
public class TorRoundTripTimeTests {
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends Reporter {
private Map<String, String> results;
@Override
public void report(long value) {
Assert.fail();
}
public Map<String, String> hasResults() {
return results;
}
@Override
public void report(Map<String, String> values) {
results = values;
}
@Override
public void report(Map<String, String> values, String prefix) {
report(values);
}
@Override
public void report(String key, String value, String timestamp, String prefix) {
}
@Override
public void report(long value, String prefix) {
report(value);
}
}
private static final File workingDirectory = new File(TorRoundTripTimeTests.class.getSimpleName());
@BeforeAll
public static void setup() throws TorCtlException {
// simulate the tor instance available to all metrics
Tor.setDefault(new NativeTor(workingDirectory));
}
@ParameterizedTest
@ValueSource(strings = {"default", "3", "4", "10"})
public void run(String sampleSize) throws Exception {
DummyReporter reporter = new DummyReporter();
// configure
Properties configuration = new Properties();
configuration.put("TorRoundTripTime.enabled", "true");
configuration.put("TorRoundTripTime.run.interval", "2");
if (!"default".equals(sampleSize))
configuration.put("TorRoundTripTime.run.sampleSize", sampleSize);
// torproject.org hidden service
configuration.put("TorRoundTripTime.run.hosts", "http://expyuzz4wqqyqhjn.onion:80");
Metric DUT = new TorRoundTripTime(reporter);
// start
DUT.configure(configuration);
// give it some time to start and then stop
Thread.sleep(100);
Metric.haltAllMetrics();
// observe results
Map<String, String> results = reporter.hasResults();
Assert.assertFalse(results.isEmpty());
Assert.assertEquals(results.get("sampleSize"), sampleSize.equals("default") ? "1" : sampleSize);
Integer p25 = Integer.valueOf(results.get("p25"));
Integer p50 = Integer.valueOf(results.get("p50"));
Integer p75 = Integer.valueOf(results.get("p75"));
Integer min = Integer.valueOf(results.get("min"));
Integer max = Integer.valueOf(results.get("max"));
Integer average = Integer.valueOf(results.get("average"));
Assert.assertTrue(0 < min);
Assert.assertTrue(min <= p25 && p25 <= p50);
Assert.assertTrue(p50 <= p75);
Assert.assertTrue(p75 <= max);
Assert.assertTrue(min <= average && average <= max);
}
@AfterAll
public static void cleanup() {
Tor tor = Tor.getDefault();
checkNotNull(tor, "tor must not be null");
tor.shutdown();
workingDirectory.delete();
}
}

View File

@ -1,92 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.monitor;
import bisq.monitor.metric.TorStartupTime;
import java.util.Map;
import java.util.Properties;
import org.junit.Assert;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled // Ignore for normal test runs as the tests take lots of time
public class TorStartupTimeTests {
/**
* A dummy Reporter for development purposes.
*/
private class DummyReporter extends Reporter {
private long result;
@Override
public void report(long value) {
result = value;
}
public long results() {
return result;
}
@Override
public void report(Map<String, String> values) {
report(Long.parseLong(values.values().iterator().next()));
}
@Override
public void report(Map<String, String> values, String prefix) {
report(values);
}
@Override
public void report(String key, String value, String timestamp, String prefix) {
}
@Override
public void report(long value, String prefix) {
report(value);
}
}
@Test
public void run() throws Exception {
DummyReporter reporter = new DummyReporter();
// configure
Properties configuration = new Properties();
configuration.put("TorStartupTime.enabled", "true");
configuration.put("TorStartupTime.run.interval", "2");
configuration.put("TorStartupTime.run.socksPort", "9999");
Metric DUT = new TorStartupTime(reporter);
// start
DUT.configure(configuration);
// give it some time and then stop
Thread.sleep(15 * 1000);
Metric.haltAllMetrics();
// TODO Test fails due timing issue
// observe results
Assert.assertTrue(reporter.results() > 0);
}
}

View File

@ -1,11 +0,0 @@
#!/bin/sh
echo "[*] Stopping Bisq Server monitoring utensils"
echo ' '
echo 'This script will not remove any configuration or binaries from the system. It just stops the services.'
sleep 10
sudo systemctl stop nginx
sudo systemctl stop collectd
sudo systemctl disable nginx
sudo systemctl disable collectd
echo "[*] Done!"

View File

@ -6,7 +6,6 @@ include 'core'
include 'cli'
include 'daemon'
include 'desktop'
include 'monitor'
include 'seednode'
include 'statsnode'
include 'apitest'