forked from Qortal/qortal
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ce33abcade | ||
|
6c5fedd456 | ||
|
afc2884707 | ||
|
18f15d8122 | ||
|
9710d67cce | ||
|
b2ef503fa7 | ||
|
a497edc488 | ||
|
185f3f515b | ||
|
a445fdc8f2 | ||
|
61e57f9672 | ||
|
fabfed552e | ||
|
c79a830f2e | ||
|
1d9347ed23 | ||
|
c4908678be | ||
|
706dc03b3e | ||
|
f0d4c1e8de | ||
|
32460a1b45 | ||
|
4df05364f5 | ||
|
9f3c1f1cf1 | ||
|
0c8c722097 |
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<metadata modelVersion="1.1.0">
|
||||||
|
<groupId>io.reticulum</groupId>
|
||||||
|
<artifactId>reticulum-network-stack</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<versioning>
|
||||||
|
<snapshot>
|
||||||
|
<localCopy>true</localCopy>
|
||||||
|
</snapshot>
|
||||||
|
<lastUpdated>20240814140435</lastUpdated>
|
||||||
|
<snapshotVersions>
|
||||||
|
<snapshotVersion>
|
||||||
|
<extension>jar</extension>
|
||||||
|
<value>1.0-SNAPSHOT</value>
|
||||||
|
<updated>20240814140435</updated>
|
||||||
|
</snapshotVersion>
|
||||||
|
<snapshotVersion>
|
||||||
|
<extension>pom</extension>
|
||||||
|
<value>1.0-SNAPSHOT</value>
|
||||||
|
<updated>20240324170649</updated>
|
||||||
|
</snapshotVersion>
|
||||||
|
</snapshotVersions>
|
||||||
|
</versioning>
|
||||||
|
</metadata>
|
Binary file not shown.
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>io.reticulum</groupId>
|
||||||
|
<artifactId>reticulum-network-stack</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<description>POM was created from install:install-file</description>
|
||||||
|
</project>
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<metadata>
|
||||||
|
<groupId>io.reticulum</groupId>
|
||||||
|
<artifactId>reticulum-network-stack</artifactId>
|
||||||
|
<versioning>
|
||||||
|
<versions>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</versions>
|
||||||
|
<lastUpdated>20240814140435</lastUpdated>
|
||||||
|
</versioning>
|
||||||
|
</metadata>
|
200
pom.xml
200
pom.xml
@ -8,7 +8,6 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
|
|
||||||
<altcoinj.version>7dc8c6f</altcoinj.version>
|
<altcoinj.version>7dc8c6f</altcoinj.version>
|
||||||
<bitcoinj.version>0.15.10</bitcoinj.version>
|
<bitcoinj.version>0.15.10</bitcoinj.version>
|
||||||
<bouncycastle.version>1.70</bouncycastle.version>
|
<bouncycastle.version>1.70</bouncycastle.version>
|
||||||
@ -45,7 +44,8 @@
|
|||||||
<maven-dependency-plugin.version>3.6.1</maven-dependency-plugin.version>
|
<maven-dependency-plugin.version>3.6.1</maven-dependency-plugin.version>
|
||||||
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
|
||||||
<maven-package-info-plugin.version>1.1.0</maven-package-info-plugin.version>
|
<maven-package-info-plugin.version>1.1.0</maven-package-info-plugin.version>
|
||||||
<maven-plugin.version>2.16.2</maven-plugin.version>
|
<!--<maven-plugin.version>2.16.2</maven-plugin.version>-->
|
||||||
|
<maven-plugin.version>3.12.1</maven-plugin.version>
|
||||||
<maven-reproducible-build-plugin.version>0.16</maven-reproducible-build-plugin.version>
|
<maven-reproducible-build-plugin.version>0.16</maven-reproducible-build-plugin.version>
|
||||||
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
|
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
|
||||||
<maven-shade-plugin.version>3.6.0</maven-shade-plugin.version>
|
<maven-shade-plugin.version>3.6.0</maven-shade-plugin.version>
|
||||||
@ -53,11 +53,15 @@
|
|||||||
<protobuf.version>3.25.3</protobuf.version>
|
<protobuf.version>3.25.3</protobuf.version>
|
||||||
<replacer.version>1.5.3</replacer.version>
|
<replacer.version>1.5.3</replacer.version>
|
||||||
<simplemagic.version>1.17</simplemagic.version>
|
<simplemagic.version>1.17</simplemagic.version>
|
||||||
<slf4j.version>1.7.36</slf4j.version>
|
|
||||||
<swagger-api.version>2.0.10</swagger-api.version>
|
<swagger-api.version>2.0.10</swagger-api.version>
|
||||||
<swagger-ui.version>5.17.14</swagger-ui.version>
|
<swagger-ui.version>5.17.14</swagger-ui.version>
|
||||||
<upnp.version>1.2</upnp.version>
|
<upnp.version>1.2</upnp.version>
|
||||||
<xz.version>1.9</xz.version>
|
<xz.version>1.9</xz.version>
|
||||||
|
<lombok.version>1.18.30</lombok.version>
|
||||||
|
<jackson.version>2.16.1</jackson.version>
|
||||||
|
<slf4j.version>2.0.12</slf4j.version>
|
||||||
|
<nitrite.version>4.3.0</nitrite.version>
|
||||||
|
<junit.version>5.9.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
<build>
|
<build>
|
||||||
<sourceDirectory>src/main/java</sourceDirectory>
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
@ -426,13 +430,41 @@
|
|||||||
<id>project.local</id>
|
<id>project.local</id>
|
||||||
<name>project</name>
|
<name>project</name>
|
||||||
<url>file:${project.basedir}/lib</url>
|
<url>file:${project.basedir}/lib</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<updatePolicy>always</updatePolicy>
|
||||||
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
<!-- jitpack for build-on-demand of altcoinj -->
|
<!-- jitpack for build-on-demand of altcoinj -->
|
||||||
<repository>
|
<repository>
|
||||||
<id>jitpack.io</id>
|
<id>jitpack.io</id>
|
||||||
<url>https://jitpack.io</url>
|
<url>https://jitpack.io</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
<updatePolicy>always</updatePolicy>
|
||||||
|
</snapshots>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
<!--
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dizitart</groupId>
|
||||||
|
<artifactId>nitrite-bom</artifactId>
|
||||||
|
<version>${nitrite.version}</version>
|
||||||
|
<scope>import</scope>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-bom</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
<scope>import</scope>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
-->
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- https://mvnrepository.com/artifact/org.codehaus.mojo/build-helper-maven-plugin -->
|
<!-- https://mvnrepository.com/artifact/org.codehaus.mojo/build-helper-maven-plugin -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -558,7 +590,17 @@
|
|||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
<version>${guava.version}</version>
|
<version>${guava.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
<!-- Logging: log4j2 -->
|
<!-- Logging: log4j2 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
<artifactId>log4j-slf4j2-impl</artifactId>
|
||||||
|
<version>${log4j.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
@ -569,24 +611,11 @@
|
|||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>${log4j.version}</version>
|
<version>${log4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- redirect slf4j to log4j2 -->
|
<dependency>
|
||||||
<dependency>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<artifactId>log4j-jul</artifactId>
|
||||||
<artifactId>log4j-slf4j-impl</artifactId>
|
<version>${log4j.version}</version>
|
||||||
<version>${log4j.version}</version>
|
</dependency>
|
||||||
</dependency>
|
|
||||||
<!-- redirect java.utils.logging to log4j2 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
|
||||||
<artifactId>log4j-jul</artifactId>
|
|
||||||
<version>${log4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- Logging: slf4j used by Jetty/Jersey -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
<version>${slf4j.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- Servlet related -->
|
<!-- Servlet related -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
@ -728,6 +757,11 @@
|
|||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bctls-jdk15on</artifactId>
|
<artifactId>bctls-jdk15on</artifactId>
|
||||||
<version>${bouncycastle.version}</version>
|
<version>${bouncycastle.version}</version>
|
||||||
|
</dependency><!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15to18 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jsoup</groupId>
|
<groupId>org.jsoup</groupId>
|
||||||
@ -770,5 +804,129 @@
|
|||||||
<artifactId>jaxb-runtime</artifactId>
|
<artifactId>jaxb-runtime</artifactId>
|
||||||
<version>${jaxb-runtime.version}</version>
|
<version>${jaxb-runtime.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- reticulum_network_stack -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.reticulum</groupId>
|
||||||
|
<artifactId>reticulum-network-stack</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>1.16.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- already declared earlier
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.14.0</version>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
<version>4.4</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- already declared earlier
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-compress</artifactId>
|
||||||
|
<version>1.26.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
</dependency>
|
||||||
|
-->
|
||||||
|
<!-- note: covered ? -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-yaml</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
<version>2.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.msgpack</groupId>
|
||||||
|
<artifactId>jackson-dataformat-msgpack</artifactId>
|
||||||
|
<version>0.9.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
|
<version>1.70</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.macasaet.fernet</groupId>
|
||||||
|
<artifactId>fernet-java8</artifactId>
|
||||||
|
<version>1.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.igormaznitsa</groupId>
|
||||||
|
<artifactId>jbbp</artifactId>
|
||||||
|
<version>2.0.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-all</artifactId>
|
||||||
|
<!--<version>4.1.106.Final</version>-->
|
||||||
|
<version>5.0.0.Alpha2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.seancfoley</groupId>
|
||||||
|
<artifactId>ipaddress</artifactId>
|
||||||
|
<version>5.4.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Nitrite Modules -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dizitart</groupId>
|
||||||
|
<artifactId>nitrite</artifactId>
|
||||||
|
<version>${nitrite.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dizitart</groupId>
|
||||||
|
<artifactId>nitrite-mvstore-adapter</artifactId>
|
||||||
|
<version>${nitrite.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>2.3.230</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2-mvstore</artifactId>
|
||||||
|
<version>2.3.230</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>5.10.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -31,6 +31,7 @@ import org.qortal.globalization.Translator;
|
|||||||
import org.qortal.gui.Gui;
|
import org.qortal.gui.Gui;
|
||||||
import org.qortal.gui.SysTray;
|
import org.qortal.gui.SysTray;
|
||||||
import org.qortal.network.Network;
|
import org.qortal.network.Network;
|
||||||
|
import org.qortal.network.RNSNetwork;
|
||||||
import org.qortal.network.Peer;
|
import org.qortal.network.Peer;
|
||||||
import org.qortal.network.message.*;
|
import org.qortal.network.message.*;
|
||||||
import org.qortal.repository.*;
|
import org.qortal.repository.*;
|
||||||
@ -115,6 +116,7 @@ public class Controller extends Thread {
|
|||||||
private long repositoryCheckpointTimestamp = startTime; // ms
|
private long repositoryCheckpointTimestamp = startTime; // ms
|
||||||
private long prunePeersTimestamp = startTime; // ms
|
private long prunePeersTimestamp = startTime; // ms
|
||||||
private long ntpCheckTimestamp = startTime; // ms
|
private long ntpCheckTimestamp = startTime; // ms
|
||||||
|
private long pruneRNSPeersTimestamp = startTime; // ms
|
||||||
private long deleteExpiredTimestamp = startTime + DELETE_EXPIRED_INTERVAL; // ms
|
private long deleteExpiredTimestamp = startTime + DELETE_EXPIRED_INTERVAL; // ms
|
||||||
|
|
||||||
/** Whether we can mint new blocks, as reported by BlockMinter. */
|
/** Whether we can mint new blocks, as reported by BlockMinter. */
|
||||||
@ -481,6 +483,15 @@ public class Controller extends Thread {
|
|||||||
return; // Not System.exit() so that GUI can display error
|
return; // Not System.exit() so that GUI can display error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Starting Reticulum");
|
||||||
|
try {
|
||||||
|
RNSNetwork rns = RNSNetwork.getInstance();
|
||||||
|
rns.start();
|
||||||
|
LOGGER.debug("Reticulum instance: {}", rns.toString());
|
||||||
|
} catch (IOException | DataException e) {
|
||||||
|
LOGGER.error("Unable to start Reticulum", e);
|
||||||
|
}
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -609,6 +620,8 @@ public class Controller extends Thread {
|
|||||||
final long repositoryCheckpointInterval = Settings.getInstance().getRepositoryCheckpointInterval();
|
final long repositoryCheckpointInterval = Settings.getInstance().getRepositoryCheckpointInterval();
|
||||||
long repositoryMaintenanceInterval = getRandomRepositoryMaintenanceInterval();
|
long repositoryMaintenanceInterval = getRandomRepositoryMaintenanceInterval();
|
||||||
final long prunePeersInterval = 5 * 60 * 1000L; // Every 5 minutes
|
final long prunePeersInterval = 5 * 60 * 1000L; // Every 5 minutes
|
||||||
|
//final long pruneRNSPeersInterval = 5 * 60 * 1000L; // Every 5 minutes
|
||||||
|
final long pruneRNSPeersInterval = 1 * 60 * 1000L; // Every 1 minute (during development)
|
||||||
|
|
||||||
// Start executor service for trimming or pruning
|
// Start executor service for trimming or pruning
|
||||||
PruneManager.getInstance().start();
|
PruneManager.getInstance().start();
|
||||||
@ -717,6 +730,18 @@ public class Controller extends Thread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Q: Do we need global pruning?
|
||||||
|
if (now >= pruneRNSPeersTimestamp + pruneRNSPeersInterval) {
|
||||||
|
pruneRNSPeersTimestamp = now + pruneRNSPeersInterval;
|
||||||
|
|
||||||
|
try {
|
||||||
|
LOGGER.debug("Pruning Reticulum peers...");
|
||||||
|
RNSNetwork.getInstance().prunePeers();
|
||||||
|
} catch (DataException e) {
|
||||||
|
LOGGER.warn(String.format("Repository issue when trying to prune Reticulum peers: %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Delete expired transactions
|
// Delete expired transactions
|
||||||
if (now >= deleteExpiredTimestamp) {
|
if (now >= deleteExpiredTimestamp) {
|
||||||
deleteExpiredTimestamp = now + DELETE_EXPIRED_INTERVAL;
|
deleteExpiredTimestamp = now + DELETE_EXPIRED_INTERVAL;
|
||||||
@ -1015,6 +1040,9 @@ public class Controller extends Thread {
|
|||||||
LOGGER.info("Shutting down networking");
|
LOGGER.info("Shutting down networking");
|
||||||
Network.getInstance().shutdown();
|
Network.getInstance().shutdown();
|
||||||
|
|
||||||
|
LOGGER.info("Shutting down Reticulum");
|
||||||
|
RNSNetwork.getInstance().shutdown();
|
||||||
|
|
||||||
LOGGER.info("Shutting down controller");
|
LOGGER.info("Shutting down controller");
|
||||||
this.interrupt();
|
this.interrupt();
|
||||||
try {
|
try {
|
||||||
|
23
src/main/java/org/qortal/network/RNSCommon.java
Normal file
23
src/main/java/org/qortal/network/RNSCommon.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package org.qortal.network;
|
||||||
|
|
||||||
|
public class RNSCommon {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination application name
|
||||||
|
*/
|
||||||
|
public static String APP_NAME = "qortal";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration path relative to the Qortal launch directory
|
||||||
|
*/
|
||||||
|
public static String defaultRNSConfigPath = new String(".reticulum");
|
||||||
|
|
||||||
|
///**
|
||||||
|
// * Qortal RNS Destinations
|
||||||
|
// */
|
||||||
|
//public enum RNSDestinations {
|
||||||
|
// BASE,
|
||||||
|
// QDN;
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
637
src/main/java/org/qortal/network/RNSNetwork.java
Normal file
637
src/main/java/org/qortal/network/RNSNetwork.java
Normal file
@ -0,0 +1,637 @@
|
|||||||
|
package org.qortal.network;
|
||||||
|
|
||||||
|
import io.reticulum.Reticulum;
|
||||||
|
import io.reticulum.Transport;
|
||||||
|
import io.reticulum.interfaces.ConnectionInterface;
|
||||||
|
import io.reticulum.destination.Destination;
|
||||||
|
import io.reticulum.destination.DestinationType;
|
||||||
|
import io.reticulum.destination.Direction;
|
||||||
|
import io.reticulum.destination.ProofStrategy;
|
||||||
|
import io.reticulum.identity.Identity;
|
||||||
|
import io.reticulum.link.Link;
|
||||||
|
import io.reticulum.link.LinkStatus;
|
||||||
|
//import io.reticulum.constant.LinkConstant;
|
||||||
|
import io.reticulum.packet.Packet;
|
||||||
|
import io.reticulum.packet.PacketReceipt;
|
||||||
|
import io.reticulum.packet.PacketReceiptStatus;
|
||||||
|
import io.reticulum.transport.AnnounceHandler;
|
||||||
|
import static io.reticulum.link.TeardownSession.DESTINATION_CLOSED;
|
||||||
|
import static io.reticulum.link.TeardownSession.INITIATOR_CLOSED;
|
||||||
|
import static io.reticulum.link.TeardownSession.TIMEOUT;
|
||||||
|
import static io.reticulum.link.LinkStatus.ACTIVE;
|
||||||
|
import static io.reticulum.link.LinkStatus.STALE;
|
||||||
|
import static io.reticulum.link.LinkStatus.PENDING;
|
||||||
|
import static io.reticulum.link.LinkStatus.HANDSHAKE;
|
||||||
|
//import static io.reticulum.packet.PacketContextType.LINKCLOSE;
|
||||||
|
import static io.reticulum.identity.IdentityKnownDestination.recall;
|
||||||
|
import static io.reticulum.utils.IdentityUtils.concatArrays;
|
||||||
|
//import static io.reticulum.constant.ReticulumConstant.TRUNCATED_HASHLENGTH;
|
||||||
|
import static io.reticulum.constant.ReticulumConstant.CONFIG_FILE_NAME;
|
||||||
|
import lombok.Data;
|
||||||
|
//import lombok.Setter;
|
||||||
|
//import lombok.Getter;
|
||||||
|
import lombok.Synchronized;
|
||||||
|
|
||||||
|
import org.qortal.repository.DataException;
|
||||||
|
import org.qortal.settings.Settings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import static java.nio.file.StandardOpenOption.CREATE;
|
||||||
|
import static java.nio.file.StandardOpenOption.WRITE;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
//import static java.util.Objects.isNull;
|
||||||
|
//import static java.util.Objects.isNull;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
|
//import static org.apache.commons.lang3.BooleanUtils.isFalse;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
//import java.util.Random;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
|
||||||
|
// logging
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
//import org.slf4j.Logger;
|
||||||
|
//import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Slf4j
|
||||||
|
public class RNSNetwork {
|
||||||
|
|
||||||
|
Reticulum reticulum;
|
||||||
|
//private static final String APP_NAME = "qortal";
|
||||||
|
static final String APP_NAME = RNSCommon.APP_NAME;
|
||||||
|
static final String defaultConfigPath = new String(".reticulum"); // if empty will look in Reticulums default paths
|
||||||
|
//static final String defaultConfigPath = RNSCommon.defaultRNSConfigPath;
|
||||||
|
//private final String defaultConfigPath = Settings.getInstance().getDefaultRNSConfigPathForReticulum();
|
||||||
|
private static Integer MAX_PEERS = 12;
|
||||||
|
//private final Integer MAX_PEERS = Settings.getInstance().getMaxReticulumPeers();
|
||||||
|
private static Integer MIN_DESIRED_PEERS = 3;
|
||||||
|
//private final Integer MIN_DESIRED_PEERS = Settings.getInstance().getMinDesiredPeers();
|
||||||
|
Identity serverIdentity;
|
||||||
|
public Destination baseDestination;
|
||||||
|
private volatile boolean isShuttingDown = false;
|
||||||
|
private final List<RNSPeer> linkedPeers = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
private final List<Link> incomingLinks = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
////private final ExecuteProduceConsume rnsNetworkEPC;
|
||||||
|
//private static final long NETWORK_EPC_KEEPALIVE = 1000L; // 1 second
|
||||||
|
//private volatile boolean isShuttingDown = false;
|
||||||
|
//private int totalThreadCount = 0;
|
||||||
|
//// TODO: settings - MaxReticulumPeers, MaxRNSNetworkThreadPoolSize (if needed)
|
||||||
|
|
||||||
|
//private static final Logger logger = LoggerFactory.getLogger(RNSNetwork.class);
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
private RNSNetwork () {
|
||||||
|
log.info("RNSNetwork constructor");
|
||||||
|
try {
|
||||||
|
//String configPath = new java.io.File(defaultConfigPath).getCanonicalPath();
|
||||||
|
log.info("creating config from {}", defaultConfigPath);
|
||||||
|
initConfig(defaultConfigPath);
|
||||||
|
//reticulum = new Reticulum(configPath);
|
||||||
|
reticulum = new Reticulum(defaultConfigPath);
|
||||||
|
var identitiesPath = reticulum.getStoragePath().resolve("identities");
|
||||||
|
if (Files.notExists(identitiesPath)) {
|
||||||
|
Files.createDirectories(identitiesPath);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("unable to create Reticulum network", e);
|
||||||
|
}
|
||||||
|
log.info("reticulum instance created");
|
||||||
|
log.info("reticulum instance created: {}", reticulum);
|
||||||
|
|
||||||
|
// Settings.getInstance().getMaxRNSNetworkThreadPoolSize(), // statically set to 5 below
|
||||||
|
//ExecutorService RNSNetworkExecutor = new ThreadPoolExecutor(1,
|
||||||
|
// 5,
|
||||||
|
// NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS,
|
||||||
|
// new SynchronousQueue<Runnable>(),
|
||||||
|
// new NamedThreadFactory("RNSNetwork-EPC"));
|
||||||
|
//rnsNetworkEPC = new RNSNetworkProcessor(RNSNetworkExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: potentially create persistent serverIdentity (utility rnid) and load it from file
|
||||||
|
public void start() throws IOException, DataException {
|
||||||
|
|
||||||
|
// create identity either from file or new (creating new keys)
|
||||||
|
var serverIdentityPath = reticulum.getStoragePath().resolve("identities/"+APP_NAME);
|
||||||
|
if (Files.isReadable(serverIdentityPath)) {
|
||||||
|
serverIdentity = Identity.fromFile(serverIdentityPath);
|
||||||
|
log.info("server identity loaded from file {}", serverIdentityPath.toString());
|
||||||
|
} else {
|
||||||
|
serverIdentity = new Identity();
|
||||||
|
log.info("APP_NAME: {}, storage path: {}", APP_NAME, serverIdentityPath);
|
||||||
|
log.info("new server identity created dynamically.");
|
||||||
|
// save it back to file by default for next start (possibly add setting to override)
|
||||||
|
try {
|
||||||
|
Files.write(serverIdentityPath, serverIdentity.getPrivateKey(), CREATE, WRITE);
|
||||||
|
log.info("serverIdentity written back to file");
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error while saving serverIdentity to {}", serverIdentityPath, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug("Server Identity: {}", serverIdentity.toString());
|
||||||
|
|
||||||
|
// show the ifac_size of the configured interfaces (debug code)
|
||||||
|
for (ConnectionInterface i: Transport.getInstance().getInterfaces() ) {
|
||||||
|
log.info("interface {}, length: {}", i.getInterfaceName(), i.getIfacSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
baseDestination = new Destination(
|
||||||
|
serverIdentity,
|
||||||
|
Direction.IN,
|
||||||
|
DestinationType.SINGLE,
|
||||||
|
APP_NAME,
|
||||||
|
"core"
|
||||||
|
);
|
||||||
|
//// idea for other entry point
|
||||||
|
//dataDestination = new Destination(
|
||||||
|
// serverIdentity,
|
||||||
|
// Direction.IN,
|
||||||
|
// DestinationType.SINGLE,
|
||||||
|
// APP_NAME,
|
||||||
|
// "core",
|
||||||
|
// "qdn"
|
||||||
|
//);
|
||||||
|
log.info("Destination "+Hex.encodeHexString(baseDestination.getHash())+" "+baseDestination.getName()+" running.");
|
||||||
|
|
||||||
|
baseDestination.setProofStrategy(ProofStrategy.PROVE_ALL);
|
||||||
|
baseDestination.setAcceptLinkRequests(true);
|
||||||
|
|
||||||
|
baseDestination.setLinkEstablishedCallback(this::clientConnected);
|
||||||
|
|
||||||
|
Transport.getInstance().registerAnnounceHandler(new QAnnounceHandler());
|
||||||
|
log.debug("announceHandlers: {}", Transport.getInstance().getAnnounceHandlers());
|
||||||
|
|
||||||
|
// do a first announce
|
||||||
|
baseDestination.announce();
|
||||||
|
log.debug("Sent initial announce from {} ({})", Hex.encodeHexString(baseDestination.getHash()), baseDestination.getName());
|
||||||
|
|
||||||
|
// Start up first networking thread (the "server loop")
|
||||||
|
//rnsNetworkEPC.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConfig(String configDir) throws IOException {
|
||||||
|
File configDir1 = new File(defaultConfigPath);
|
||||||
|
if (!configDir1.exists()) {
|
||||||
|
configDir1.mkdir();
|
||||||
|
}
|
||||||
|
var configPath = Path.of(configDir1.getAbsolutePath());
|
||||||
|
Path configFile = configPath.resolve(CONFIG_FILE_NAME);
|
||||||
|
|
||||||
|
if (Files.notExists(configFile)) {
|
||||||
|
var defaultConfig = this.getClass().getClassLoader().getResourceAsStream("reticulum_default_config.yml");
|
||||||
|
Files.copy(defaultConfig, configFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
isShuttingDown = true;
|
||||||
|
log.info("shutting down Reticulum");
|
||||||
|
|
||||||
|
// Stop processing threads (the "server loop")
|
||||||
|
//try {
|
||||||
|
// if (!this.rnsNetworkEPC.shutdown(5000)) {
|
||||||
|
// logger.warn("RNSNetwork threads failed to terminate");
|
||||||
|
// }
|
||||||
|
//} catch (InterruptedException e) {
|
||||||
|
// logger.warn("Interrupted while waiting for RNS networking threads to terminate");
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Disconnect peers gracefully and terminate Reticulum
|
||||||
|
for (RNSPeer p: linkedPeers) {
|
||||||
|
log.info("shutting down peer: {}", Hex.encodeHexString(p.getDestinationHash()));
|
||||||
|
log.debug("peer: {}", p);
|
||||||
|
p.shutdown();
|
||||||
|
try {
|
||||||
|
TimeUnit.SECONDS.sleep(1); // allow for peers to disconnect gracefully
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("exception: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// gracefully close links of peers that point to us
|
||||||
|
for (Link l: incomingLinks) {
|
||||||
|
sendCloseToRemote(l);
|
||||||
|
}
|
||||||
|
// Note: we still need to get the packet timeout callback to work...
|
||||||
|
reticulum.exitHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendCloseToRemote(Link link) {
|
||||||
|
if (nonNull(link)) {
|
||||||
|
var data = concatArrays("close::".getBytes(UTF_8),link.getDestination().getHash());
|
||||||
|
Packet closePacket = new Packet(link, data);
|
||||||
|
var packetReceipt = closePacket.send();
|
||||||
|
packetReceipt.setDeliveryCallback(this::closePacketDelivered);
|
||||||
|
packetReceipt.setTimeoutCallback(this::packetTimedOut);
|
||||||
|
} else {
|
||||||
|
log.debug("can't send to null link");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closePacketDelivered(PacketReceipt receipt) {
|
||||||
|
var rttString = new String("");
|
||||||
|
if (receipt.getStatus() == PacketReceiptStatus.DELIVERED) {
|
||||||
|
var rtt = receipt.getRtt(); // rtt (Java) is in miliseconds
|
||||||
|
//log.info("qqp - packetDelivered - rtt: {}", rtt);
|
||||||
|
if (rtt >= 1000) {
|
||||||
|
rtt = Math.round(rtt / 1000);
|
||||||
|
rttString = String.format("%d seconds", rtt);
|
||||||
|
} else {
|
||||||
|
rttString = String.format("%d miliseconds", rtt);
|
||||||
|
}
|
||||||
|
log.info("Shutdown packet confirmation received from {}, round-trip time is {}",
|
||||||
|
Hex.encodeHexString(receipt.getDestination().getHash()), rttString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void packetTimedOut(PacketReceipt receipt) {
|
||||||
|
log.info("packet timed out, receipt status: {}", receipt.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clientConnected(Link link) {
|
||||||
|
link.setLinkClosedCallback(this::clientDisconnected);
|
||||||
|
link.setPacketCallback(this::serverPacketReceived);
|
||||||
|
var peer = findPeerByLink(link);
|
||||||
|
if (nonNull(peer)) {
|
||||||
|
log.info("initiator peer {} opened link (link lookup: {}), link destination hash: {}",
|
||||||
|
Hex.encodeHexString(peer.getDestinationHash()), link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
// make sure the peerLink is actvive.
|
||||||
|
peer.getOrInitPeerLink();
|
||||||
|
} else {
|
||||||
|
log.info("non-initiator closed link (link lookup: {}), link destination hash (initiator): {}",
|
||||||
|
peer, link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
}
|
||||||
|
incomingLinks.add(link);
|
||||||
|
log.info("***> Client connected, link: {}", link);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clientDisconnected(Link link) {
|
||||||
|
var peer = findPeerByLink(link);
|
||||||
|
if (nonNull(peer)) {
|
||||||
|
log.info("initiator peer {} closed link (link lookup: {}), link destination hash: {}",
|
||||||
|
Hex.encodeHexString(peer.getDestinationHash()), link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
} else {
|
||||||
|
log.info("non-initiator closed link (link lookup: {}), link destination hash (initiator): {}",
|
||||||
|
peer, link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
}
|
||||||
|
// if we have a peer pointing to that destination, we can close and remove it
|
||||||
|
peer = findPeerByDestinationHash(link.getDestination().getHash());
|
||||||
|
if (nonNull(peer)) {
|
||||||
|
// Note: no shutdown as the remobe peer could be only rebooting.
|
||||||
|
// keep it to reopen link later if possible.
|
||||||
|
peer.getPeerLink().teardown();
|
||||||
|
}
|
||||||
|
incomingLinks.remove(link);
|
||||||
|
log.info("***> Client disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serverPacketReceived(byte[] message, Packet packet) {
|
||||||
|
var msgText = new String(message, StandardCharsets.UTF_8);
|
||||||
|
log.info("Received data on link - message: {}, destinationHash: {}", msgText, Hex.encodeHexString(packet.getDestinationHash()));
|
||||||
|
//var peer = findPeerByDestinationHash(packet.getDestinationHash());
|
||||||
|
//if (msgText.equals("ping")) {
|
||||||
|
// log.info("received ping");
|
||||||
|
// //if (nonNull(peer)) {
|
||||||
|
// // String replyText = "pong";
|
||||||
|
// // byte[] replyData = replyText.getBytes(StandardCharsets.UTF_8);
|
||||||
|
// // Packet reply = new Packet(peer.getPeerLink(), replyData);
|
||||||
|
// //}
|
||||||
|
//}
|
||||||
|
//if (msgText.equals("shutdown")) {
|
||||||
|
// log.info("shutdown packet received");
|
||||||
|
// var link = recall(packet.getDestinationHash());
|
||||||
|
// log.info("recalled destinationHash: {}", link);
|
||||||
|
// //...
|
||||||
|
//}
|
||||||
|
// TODO: process packet....
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void announceBaseDestination () {
|
||||||
|
// getBaseDestination().announce();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//@Slf4j
|
||||||
|
private class QAnnounceHandler implements AnnounceHandler {
|
||||||
|
@Override
|
||||||
|
public String getAspectFilter() {
|
||||||
|
// handle all announces
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Synchronized
|
||||||
|
public void receivedAnnounce(byte[] destinationHash, Identity announcedIdentity, byte[] appData) {
|
||||||
|
var peerExists = false;
|
||||||
|
var activePeerCount = 0;
|
||||||
|
|
||||||
|
log.info("Received an announce from {}", Hex.encodeHexString(destinationHash));
|
||||||
|
|
||||||
|
if (nonNull(appData)) {
|
||||||
|
log.debug("The announce contained the following app data: {}", new String(appData, UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to peer list if we can use more peers
|
||||||
|
//synchronized (this) {
|
||||||
|
var lps = RNSNetwork.getInstance().getLinkedPeers();
|
||||||
|
for (RNSPeer p: lps) {
|
||||||
|
var pl = p.getPeerLink();
|
||||||
|
if ((nonNull(pl) && (pl.getStatus() == ACTIVE))) {
|
||||||
|
activePeerCount = activePeerCount + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activePeerCount < MAX_PEERS) {
|
||||||
|
//if (!peerExists) {
|
||||||
|
//var peer = findPeerByDestinationHash(destinationHash);
|
||||||
|
for (RNSPeer p: lps) {
|
||||||
|
if (Arrays.equals(p.getDestinationHash(), destinationHash)) {
|
||||||
|
log.info("QAnnounceHandler - peer exists - found peer matching destinationHash");
|
||||||
|
if (nonNull(p.getPeerLink())) {
|
||||||
|
log.info("peer link: {}, status: {}", p.getPeerLink(), p.getPeerLink().getStatus());
|
||||||
|
}
|
||||||
|
peerExists = true;
|
||||||
|
if (p.getPeerLink().getStatus() != ACTIVE) {
|
||||||
|
p.getOrInitPeerLink();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (nonNull(p.getPeerLink())) {
|
||||||
|
log.info("QAnnounceHandler - other peer - link: {}, status: {}", p.getPeerLink(), p.getPeerLink().getStatus());
|
||||||
|
} else {
|
||||||
|
log.info("QAnnounceHandler - null link peer - link: {}", p.getPeerLink());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!peerExists) {
|
||||||
|
RNSPeer newPeer = new RNSPeer(destinationHash);
|
||||||
|
newPeer.setServerIdentity(announcedIdentity);
|
||||||
|
newPeer.setIsInitiator(true);
|
||||||
|
lps.add(newPeer);
|
||||||
|
log.info("added new RNSPeer, destinationHash: {}", Hex.encodeHexString(destinationHash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main thread
|
||||||
|
//class RNSNetworkProcessor extends ExecuteProduceConsume {
|
||||||
|
//
|
||||||
|
// //private final Logger logger = LoggerFactory.getLogger(RNSNetworkProcessor.class);
|
||||||
|
//
|
||||||
|
// private final AtomicLong nextConnectTaskTimestamp = new AtomicLong(0L); // ms - try first connect once NTP syncs
|
||||||
|
// private final AtomicLong nextBroadcastTimestamp = new AtomicLong(0L); // ms - try first broadcast once NTP syncs
|
||||||
|
//
|
||||||
|
// private Iterator<SelectionKey> channelIterator = null;
|
||||||
|
//
|
||||||
|
// RNSNetworkProcessor(ExecutorService executor) {
|
||||||
|
// super(executor);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// protected void onSpawnFailure() {
|
||||||
|
// // For debugging:
|
||||||
|
// // ExecutorDumper.dump(this.executor, 3, ExecuteProduceConsume.class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// protected Task produceTask(boolean canBlock) throws InterruptedException {
|
||||||
|
// Task task;
|
||||||
|
//
|
||||||
|
// //task = maybeProducePeerMessageTask();
|
||||||
|
// //if (task != null) {
|
||||||
|
// // return task;
|
||||||
|
// //}
|
||||||
|
// //
|
||||||
|
// //final Long now = NTP.getTime();
|
||||||
|
// //
|
||||||
|
// //task = maybeProducePeerPingTask(now);
|
||||||
|
// //if (task != null) {
|
||||||
|
// // return task;
|
||||||
|
// //}
|
||||||
|
// //
|
||||||
|
// //task = maybeProduceConnectPeerTask(now);
|
||||||
|
// //if (task != null) {
|
||||||
|
// // return task;
|
||||||
|
// //}
|
||||||
|
// //
|
||||||
|
// //task = maybeProduceBroadcastTask(now);
|
||||||
|
// //if (task != null) {
|
||||||
|
// // return task;
|
||||||
|
// //}
|
||||||
|
// //
|
||||||
|
// // Only this method can block to reduce CPU spin
|
||||||
|
// //return maybeProduceChannelTask(canBlock);
|
||||||
|
//
|
||||||
|
// // TODO: flesh out the tasks handled by Reticulum
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// //...TODO: implement abstract methods...
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// getter / setter
|
||||||
|
private static class SingletonContainer {
|
||||||
|
private static final RNSNetwork INSTANCE = new RNSNetwork();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RNSNetwork getInstance() {
|
||||||
|
return SingletonContainer.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Identity getServerIdentity() {
|
||||||
|
return this.serverIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reticulum getReticulum() {
|
||||||
|
return this.reticulum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RNSPeer> getLinkedPeers() {
|
||||||
|
synchronized(this.linkedPeers) {
|
||||||
|
//return new ArrayList<>(this.linkedPeers);
|
||||||
|
return this.linkedPeers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getTotalPeers() {
|
||||||
|
synchronized (this) {
|
||||||
|
return linkedPeers.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Destination getBaseDestination() {
|
||||||
|
return baseDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maintenance
|
||||||
|
//public void removePeer(RNSPeer peer) {
|
||||||
|
// synchronized(this) {
|
||||||
|
// List<RNSPeer> peerList = this.linkedPeers;
|
||||||
|
// log.info("removing peer {} on peer shutdown", peer);
|
||||||
|
// peerList.remove(peer);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public void pingPeer(RNSPeer peer) {
|
||||||
|
// if (nonNull(peer)) {
|
||||||
|
// peer.pingRemote();
|
||||||
|
// } else {
|
||||||
|
// log.error("peer argument is null");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
public void prunePeers() throws DataException {
|
||||||
|
// run periodically (by the Controller)
|
||||||
|
//List<Link> linkList = getLinkedPeers();
|
||||||
|
var peerList = getLinkedPeers();
|
||||||
|
log.info("number of links (linkedPeers) before prunig: {}", peerList.size());
|
||||||
|
Link pLink;
|
||||||
|
LinkStatus lStatus;
|
||||||
|
for (RNSPeer p: peerList) {
|
||||||
|
pLink = p.getPeerLink();
|
||||||
|
log.info("prunePeers - pLink: {}, destinationHash: {}",
|
||||||
|
pLink, Hex.encodeHexString(p.getDestinationHash()));
|
||||||
|
log.debug("peer: {}", p);
|
||||||
|
if (nonNull(pLink)) {
|
||||||
|
if (p.getPeerTimedOut()) {
|
||||||
|
// close peer link for now
|
||||||
|
pLink.teardown();
|
||||||
|
}
|
||||||
|
lStatus = pLink.getStatus();
|
||||||
|
log.info("Link {} status: {}", pLink, lStatus);
|
||||||
|
// lStatus in: PENDING, HANDSHAKE, ACTIVE, STALE, CLOSED
|
||||||
|
if ((lStatus == STALE) || (pLink.getTeardownReason() == TIMEOUT) || (p.getDeleteMe())) {
|
||||||
|
p.shutdown();
|
||||||
|
peerList.remove(p);
|
||||||
|
} else if (lStatus == HANDSHAKE) {
|
||||||
|
// stuck in handshake state (do we need to shutdown/remove it?)
|
||||||
|
log.info("peer status HANDSHAKE");
|
||||||
|
p.shutdown();
|
||||||
|
peerList.remove(p);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peerList.remove(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//removeExpiredPeers(this.linkedPeers);
|
||||||
|
log.info("number of links (linkedPeers) after prunig: {}", peerList.size());
|
||||||
|
//log.info("we have {} non-initiator links, list: {}", incomingLinks.size(), incomingLinks);
|
||||||
|
var activePeerCount = 0;
|
||||||
|
var lps = RNSNetwork.getInstance().getLinkedPeers();
|
||||||
|
for (RNSPeer p: lps) {
|
||||||
|
pLink = p.getPeerLink();
|
||||||
|
p.pingRemote();
|
||||||
|
try {
|
||||||
|
TimeUnit.SECONDS.sleep(2); // allow for peers to disconnect gracefully
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.error("exception: {}", e);
|
||||||
|
}
|
||||||
|
if ((nonNull(pLink) && (pLink.getStatus() == ACTIVE))) {
|
||||||
|
activePeerCount = activePeerCount + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("we have {} active peers", activePeerCount);
|
||||||
|
maybeAnnounce(getBaseDestination());
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void removeExpiredPeers(List<RNSPeer> peerList) {
|
||||||
|
// //List<RNSPeer> peerList = this.linkedPeers;
|
||||||
|
// for (RNSPeer p: peerList) {
|
||||||
|
// if (p.getPeerLink() == null) {
|
||||||
|
// peerList.remove(p);
|
||||||
|
// } else if (p.getPeerLink().getStatus() == STALE) {
|
||||||
|
// peerList.remove(p);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public void maybeAnnounce(Destination d) {
|
||||||
|
if (getLinkedPeers().size() < MIN_DESIRED_PEERS) {
|
||||||
|
d.announce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
//@Synchronized
|
||||||
|
//public RNSPeer getPeerIfExists(RNSPeer peer) {
|
||||||
|
// List<RNSPeer> lps = RNSNetwork.getInstance().getLinkedPeers();
|
||||||
|
// RNSPeer result = null;
|
||||||
|
// for (RNSPeer p: lps) {
|
||||||
|
// if (nonNull(p.getDestinationHash()) && Arrays.equals(p.getDestinationHash(), peer.getDestinationHash())) {
|
||||||
|
// log.info("found match by destinationHash");
|
||||||
|
// result = p;
|
||||||
|
// //break;
|
||||||
|
// }
|
||||||
|
// if (nonNull(p.getPeerDestinationHash()) && Arrays.equals(p.getPeerDestinationHash(), peer.getPeerDestinationHash())) {
|
||||||
|
// log.info("found match by peerDestinationHash");
|
||||||
|
// result = p;
|
||||||
|
// //break;
|
||||||
|
// }
|
||||||
|
// if (nonNull(p.getPeerBaseDestinationHash()) && Arrays.equals(p.getPeerBaseDestinationHash(), peer.getPeerBaseDestinationHash())) {
|
||||||
|
// log.info("found match by peerBaseDestinationHash");
|
||||||
|
// result = p;
|
||||||
|
// //break;
|
||||||
|
// }
|
||||||
|
// if (nonNull(p.getRemoteTestHash()) && Arrays.equals(p.getRemoteTestHash(), peer.getRemoteTestHash())) {
|
||||||
|
// log.info("found match by remoteTestHash");
|
||||||
|
// result = p;
|
||||||
|
// //break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public RNSPeer findPeerByLink(Link link) {
|
||||||
|
List<RNSPeer> lps = RNSNetwork.getInstance().getLinkedPeers();
|
||||||
|
RNSPeer peer = null;
|
||||||
|
for (RNSPeer p : lps) {
|
||||||
|
var pLink = p.getPeerLink();
|
||||||
|
if (nonNull(pLink)) {
|
||||||
|
if (Arrays.equals(pLink.getDestination().getHash(),link.getDestination().getHash())) {
|
||||||
|
log.info("found peer matching destinationHash: {}", Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
peer = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RNSPeer findPeerByDestinationHash(byte[] dhash) {
|
||||||
|
List<RNSPeer> lps = RNSNetwork.getInstance().getLinkedPeers();
|
||||||
|
RNSPeer peer = null;
|
||||||
|
for (RNSPeer p : lps) {
|
||||||
|
if (Arrays.equals(p.getDestinationHash(), dhash)) {
|
||||||
|
log.info("found peer matching destinationHash: {}", Hex.encodeHexString(dhash));
|
||||||
|
peer = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePeer(RNSPeer peer) {
|
||||||
|
List<RNSPeer> peerList = this.linkedPeers;
|
||||||
|
if (nonNull(peer)) {
|
||||||
|
peerList.remove(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
284
src/main/java/org/qortal/network/RNSPeer.java
Normal file
284
src/main/java/org/qortal/network/RNSPeer.java
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
package org.qortal.network;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static java.util.Objects.isNull;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import io.reticulum.Reticulum;
|
||||||
|
import org.qortal.network.RNSNetwork;
|
||||||
|
import io.reticulum.link.Link;
|
||||||
|
import io.reticulum.link.RequestReceipt;
|
||||||
|
import io.reticulum.packet.PacketReceiptStatus;
|
||||||
|
import io.reticulum.packet.Packet;
|
||||||
|
import io.reticulum.packet.PacketReceipt;
|
||||||
|
import io.reticulum.identity.Identity;
|
||||||
|
import io.reticulum.channel.Channel;
|
||||||
|
import io.reticulum.destination.Destination;
|
||||||
|
import io.reticulum.destination.DestinationType;
|
||||||
|
import io.reticulum.destination.Direction;
|
||||||
|
import io.reticulum.destination.ProofStrategy;
|
||||||
|
import io.reticulum.resource.Resource;
|
||||||
|
import static io.reticulum.link.TeardownSession.INITIATOR_CLOSED;
|
||||||
|
import static io.reticulum.link.TeardownSession.DESTINATION_CLOSED;
|
||||||
|
import static io.reticulum.link.TeardownSession.TIMEOUT;
|
||||||
|
import static io.reticulum.link.LinkStatus.ACTIVE;
|
||||||
|
import static io.reticulum.link.LinkStatus.CLOSED;
|
||||||
|
import static io.reticulum.identity.IdentityKnownDestination.recall;
|
||||||
|
//import static io.reticulum.identity.IdentityKnownDestination.recallAppData;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
import static org.apache.commons.lang3.ArrayUtils.subarray;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Slf4j
|
||||||
|
public class RNSPeer {
|
||||||
|
|
||||||
|
//static final String APP_NAME = "qortal";
|
||||||
|
static final String APP_NAME = RNSCommon.APP_NAME;
|
||||||
|
//static final String defaultConfigPath = new String(".reticulum");
|
||||||
|
static final String defaultConfigPath = RNSCommon.defaultRNSConfigPath;
|
||||||
|
|
||||||
|
private byte[] destinationHash; // remote destination hash
|
||||||
|
Destination peerDestination; // OUT destination created for this
|
||||||
|
private Identity serverIdentity;
|
||||||
|
@Setter(AccessLevel.PACKAGE) private Instant creationTimestamp;
|
||||||
|
private Instant lastAccessTimestamp;
|
||||||
|
Link peerLink;
|
||||||
|
private Boolean isInitiator;
|
||||||
|
private Boolean deleteMe = false;
|
||||||
|
|
||||||
|
private Double requestResponseProgress;
|
||||||
|
@Setter(AccessLevel.PACKAGE) private Boolean peerTimedOut = false;
|
||||||
|
|
||||||
|
public RNSPeer(byte[] dhash) {
|
||||||
|
destinationHash = dhash;
|
||||||
|
serverIdentity = recall(dhash);
|
||||||
|
initPeerLink();
|
||||||
|
//setCreationTimestamp(System.currentTimeMillis());
|
||||||
|
creationTimestamp = Instant.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initPeerLink() {
|
||||||
|
peerDestination = new Destination(
|
||||||
|
this.serverIdentity,
|
||||||
|
Direction.OUT,
|
||||||
|
DestinationType.SINGLE,
|
||||||
|
RNSNetwork.APP_NAME,
|
||||||
|
"core"
|
||||||
|
);
|
||||||
|
peerDestination.setProofStrategy(ProofStrategy.PROVE_ALL);
|
||||||
|
|
||||||
|
lastAccessTimestamp = Instant.now();
|
||||||
|
isInitiator = true;
|
||||||
|
|
||||||
|
peerLink = new Link(peerDestination);
|
||||||
|
|
||||||
|
this.peerLink.setLinkEstablishedCallback(this::linkEstablished);
|
||||||
|
this.peerLink.setLinkClosedCallback(this::linkClosed);
|
||||||
|
this.peerLink.setPacketCallback(this::linkPacketReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Link getOrInitPeerLink() {
|
||||||
|
if (this.peerLink.getStatus() == ACTIVE) {
|
||||||
|
lastAccessTimestamp = Instant.now();
|
||||||
|
return this.peerLink;
|
||||||
|
} else {
|
||||||
|
initPeerLink();
|
||||||
|
}
|
||||||
|
return this.peerLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
if (nonNull(peerLink)) {
|
||||||
|
log.info("shutdown - peerLink: {}, status: {}", peerLink, peerLink.getStatus());
|
||||||
|
if (peerLink.getStatus() == ACTIVE) {
|
||||||
|
peerLink.teardown();
|
||||||
|
}
|
||||||
|
this.peerLink = null;
|
||||||
|
}
|
||||||
|
this.deleteMe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Channel getChannel() {
|
||||||
|
if (isNull(getPeerLink())) {
|
||||||
|
log.warn("link is null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
setLastAccessTimestamp(Instant.now());
|
||||||
|
return getPeerLink().getChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Link callbacks */
|
||||||
|
public void linkEstablished(Link link) {
|
||||||
|
link.setLinkClosedCallback(this::linkClosed);
|
||||||
|
log.info("peerLink {} established (link: {}) with peer: hash - {}, link destination hash: {}",
|
||||||
|
peerLink, link, Hex.encodeHexString(destinationHash),
|
||||||
|
Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkClosed(Link link) {
|
||||||
|
if (link.getTeardownReason() == TIMEOUT) {
|
||||||
|
log.info("The link timed out");
|
||||||
|
this.peerTimedOut = true;
|
||||||
|
} else if (link.getTeardownReason() == INITIATOR_CLOSED) {
|
||||||
|
log.info("Link closed callback: The initiator closed the link");
|
||||||
|
log.info("peerLink {} closed (link: {}), link destination hash: {}",
|
||||||
|
peerLink, link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
} else if (link.getTeardownReason() == DESTINATION_CLOSED) {
|
||||||
|
log.info("Link closed callback: The link was closed by the peer, removing peer");
|
||||||
|
log.info("peerLink {} closed (link: {}), link destination hash: {}",
|
||||||
|
peerLink, link, Hex.encodeHexString(link.getDestination().getHash()));
|
||||||
|
} else {
|
||||||
|
log.info("Link closed callback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkPacketReceived(byte[] message, Packet packet) {
|
||||||
|
var msgText = new String(message, StandardCharsets.UTF_8);
|
||||||
|
if (msgText.equals("ping")) {
|
||||||
|
log.info("received ping on link");
|
||||||
|
} else if (msgText.startsWith("close::")) {
|
||||||
|
var targetPeerHash = subarray(message, 7, message.length);
|
||||||
|
log.info("peer dest hash: {}, target hash: {}",
|
||||||
|
Hex.encodeHexString(destinationHash),
|
||||||
|
Hex.encodeHexString(targetPeerHash));
|
||||||
|
if (Arrays.equals(destinationHash, targetPeerHash)) {
|
||||||
|
log.info("closing link: {}", peerLink.getDestination().getHexHash());
|
||||||
|
peerLink.teardown();
|
||||||
|
}
|
||||||
|
} else if (msgText.startsWith("open::")) {
|
||||||
|
var targetPeerHash = subarray(message, 7, message.length);
|
||||||
|
log.info("peer dest hash: {}, target hash: {}",
|
||||||
|
Hex.encodeHexString(destinationHash),
|
||||||
|
Hex.encodeHexString(targetPeerHash));
|
||||||
|
if (Arrays.equals(destinationHash, targetPeerHash)) {
|
||||||
|
log.info("closing link: {}", peerLink.getDestination().getHexHash());
|
||||||
|
getOrInitPeerLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: process incoming packet....
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** PacketReceipt callbacks */
|
||||||
|
public void packetDelivered(PacketReceipt receipt) {
|
||||||
|
var rttString = new String("");
|
||||||
|
//log.info("packet delivered callback, receipt: {}", receipt);
|
||||||
|
if (receipt.getStatus() == PacketReceiptStatus.DELIVERED) {
|
||||||
|
var rtt = receipt.getRtt(); // rtt (Java) is in miliseconds
|
||||||
|
//log.info("qqp - packetDelivered - rtt: {}", rtt);
|
||||||
|
if (rtt >= 1000) {
|
||||||
|
rtt = Math.round(rtt / 1000);
|
||||||
|
rttString = String.format("%d seconds", rtt);
|
||||||
|
} else {
|
||||||
|
rttString = String.format("%d miliseconds", rtt);
|
||||||
|
}
|
||||||
|
log.info("Valid reply received from {}, round-trip time is {}",
|
||||||
|
Hex.encodeHexString(receipt.getDestination().getHash()), rttString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void packetTimedOut(PacketReceipt receipt) {
|
||||||
|
log.info("packet timed out, receipt status: {}", receipt.getStatus());
|
||||||
|
if (receipt.getStatus() == PacketReceiptStatus.FAILED) {
|
||||||
|
this.peerTimedOut = true;
|
||||||
|
this.peerLink.teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Link Request callbacks */
|
||||||
|
public void linkRequestResponseReceived(RequestReceipt rr) {
|
||||||
|
log.info("Response received");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkRequestResponseProgress(RequestReceipt rr) {
|
||||||
|
this.requestResponseProgress = rr.getProgress();
|
||||||
|
log.debug("Response progress set");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkRequestFailed(RequestReceipt rr) {
|
||||||
|
log.error("Request failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Link Resource callbacks */
|
||||||
|
// Resource: allow arbitrary amounts of data to be passed over a link with
|
||||||
|
// sequencing, compression, coordination and checksumming handled automatically
|
||||||
|
//public Boolean linkResourceAdvertised(Resource resource) {
|
||||||
|
// log.debug("Resource advertised");
|
||||||
|
//}
|
||||||
|
public void linkResourceTransferStarted(Resource resource) {
|
||||||
|
log.debug("Resource transfer started");
|
||||||
|
}
|
||||||
|
public void linkResourceTransferComcluded(Resource resource) {
|
||||||
|
log.debug("Resource transfer complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Utility methods */
|
||||||
|
public void pingRemote() {
|
||||||
|
var link = this.peerLink;
|
||||||
|
if (nonNull(link)) {
|
||||||
|
if (peerLink.getStatus() == ACTIVE) {
|
||||||
|
log.info("pinging remote: {}", link);
|
||||||
|
var data = "ping".getBytes(UTF_8);
|
||||||
|
link.setPacketCallback(this::linkPacketReceived);
|
||||||
|
Packet pingPacket = new Packet(link, data);
|
||||||
|
PacketReceipt packetReceipt = pingPacket.send();
|
||||||
|
// Note: don't setTimeout, we want it to timeout with FAIL if not deliverable
|
||||||
|
//packetReceipt.setTimeout(5000L);
|
||||||
|
packetReceipt.setTimeoutCallback(this::packetTimedOut);
|
||||||
|
packetReceipt.setDeliveryCallback(this::packetDelivered);
|
||||||
|
} else {
|
||||||
|
log.info("can't send ping to a peer {} with (link) status: {}",
|
||||||
|
Hex.encodeHexString(peerLink.getDestination().getHash()), peerLink.getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//public void shutdownLink(Link link) {
|
||||||
|
// var data = "shutdown".getBytes(UTF_8);
|
||||||
|
// Packet shutdownPacket = new Packet(link, data);
|
||||||
|
// PacketReceipt packetReceipt = shutdownPacket.send();
|
||||||
|
// packetReceipt.setTimeout(2000L);
|
||||||
|
// packetReceipt.setTimeoutCallback(this::packetTimedOut);
|
||||||
|
// packetReceipt.setDeliveryCallback(this::shutdownPacketDelivered);
|
||||||
|
//}
|
||||||
|
|
||||||
|
///** check if a link is available (ACTIVE)
|
||||||
|
// * link: a certain peer link, or null (default link == link to Qortal node RNS baseDestination)
|
||||||
|
// */
|
||||||
|
//public Boolean peerLinkIsAlive(Link link) {
|
||||||
|
// var result = false;
|
||||||
|
// if (isNull(link)) {
|
||||||
|
// // default link
|
||||||
|
// var defaultLink = getLink();
|
||||||
|
// if (nonNull(defaultLink) && defaultLink.getStatus() == ACTIVE) {
|
||||||
|
// result = true;
|
||||||
|
// log.info("Default link is available");
|
||||||
|
// } else {
|
||||||
|
// log.info("Default link {} is not available, status: {}", defaultLink, defaultLink.getStatus());
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // other link (future where we have multiple destinations...)
|
||||||
|
// if (link.getStatus() == ACTIVE) {
|
||||||
|
// result = true;
|
||||||
|
// log.info("Link {} is available (status: {})", link, link.getStatus());
|
||||||
|
// } else {
|
||||||
|
// log.info("Link {} is not available, status: {}", link, link.getStatus());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
93
src/main/resources/reticulum_default_config.yml
Normal file
93
src/main/resources/reticulum_default_config.yml
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
---
|
||||||
|
# You should probably edit it to include any additional,
|
||||||
|
# interfaces and settings you might need.
|
||||||
|
|
||||||
|
# Only the most basic options are included in this default
|
||||||
|
# configuration. To see a more verbose, and much longer,
|
||||||
|
# configuration example, you can run the command:
|
||||||
|
# rnsd --exampleconfig
|
||||||
|
|
||||||
|
reticulum:
|
||||||
|
|
||||||
|
# If you enable Transport, your system will route traffic
|
||||||
|
# for other peers, pass announces and serve path requests.
|
||||||
|
# This should only be done for systems that are suited to
|
||||||
|
# act as transport nodes, ie. if they are stationary and
|
||||||
|
# always-on. This directive is optional and can be removed
|
||||||
|
# for brevity.
|
||||||
|
|
||||||
|
enable_transport: false
|
||||||
|
|
||||||
|
# By default, the first program to launch the Reticulum
|
||||||
|
# Network Stack will create a shared instance, that other
|
||||||
|
# programs can communicate with. Only the shared instance
|
||||||
|
# opens all the configured interfaces directly, and other
|
||||||
|
# local programs communicate with the shared instance over
|
||||||
|
# a local socket. This is completely transparent to the
|
||||||
|
# user, and should generally be turned on. This directive
|
||||||
|
# is optional and can be removed for brevity.
|
||||||
|
|
||||||
|
share_instance: false
|
||||||
|
|
||||||
|
# If you want to run multiple *different* shared instances
|
||||||
|
# on the same system, you will need to specify different
|
||||||
|
# shared instance ports for each. The defaults are given
|
||||||
|
# below, and again, these options can be left out if you
|
||||||
|
# don't need them.
|
||||||
|
|
||||||
|
#shared_instance_port: 37428
|
||||||
|
#instance_control_port: 37429
|
||||||
|
shared_instance_port: 37438
|
||||||
|
instance_control_port: 37439
|
||||||
|
|
||||||
|
# You can configure Reticulum to panic and forcibly close
|
||||||
|
# if an unrecoverable interface error occurs, such as the
|
||||||
|
# hardware device for an interface disappearing. This is
|
||||||
|
# an optional directive, and can be left out for brevity.
|
||||||
|
# This behaviour is disabled by default.
|
||||||
|
|
||||||
|
panic_on_interface_error: false
|
||||||
|
|
||||||
|
|
||||||
|
# The interfaces section defines the physical and virtual
|
||||||
|
# interfaces Reticulum will use to communicate on. This
|
||||||
|
# section will contain examples for a variety of interface
|
||||||
|
# types. You can modify these or use them as a basis for
|
||||||
|
# your own config, or simply remove the unused ones.
|
||||||
|
|
||||||
|
interfaces:
|
||||||
|
|
||||||
|
# This interface enables communication with other
|
||||||
|
# link-local Reticulum nodes over UDP. It does not
|
||||||
|
# need any functional IP infrastructure like routers
|
||||||
|
# or DHCP servers, but will require that at least link-
|
||||||
|
# local IPv6 is enabled in your operating system, which
|
||||||
|
# should be enabled by default in almost any OS. See
|
||||||
|
# the Reticulum Manual for more configuration options.
|
||||||
|
#"Default Interface":
|
||||||
|
# type: AutoInterface
|
||||||
|
# enabled: true
|
||||||
|
|
||||||
|
# This interface enables communication with a "backbone"
|
||||||
|
# server over TCP.
|
||||||
|
# Note: others may be added for redundancy
|
||||||
|
"TCP Client Interface mobilefabrik":
|
||||||
|
type: TCPClientInterface
|
||||||
|
enabled: true
|
||||||
|
target_host: phantom.mobilefabrik.com
|
||||||
|
target_port: 4242
|
||||||
|
#network_name: qortal
|
||||||
|
|
||||||
|
# This interface turns this Reticulum instance into a
|
||||||
|
# server other clients can connect to over TCP.
|
||||||
|
# To enable this instance to route traffic the above
|
||||||
|
# setting "enable_transport" needs to be set (to true).
|
||||||
|
# Note: this interface type is not yet supported by
|
||||||
|
# reticulum-network-stack.
|
||||||
|
#"TCP Server Interface":
|
||||||
|
# type: TCPServerInterface
|
||||||
|
# enabled: true
|
||||||
|
# listen_ip: 0.0.0.0
|
||||||
|
# listen_port: 3434
|
||||||
|
# #network_name: qortal
|
||||||
|
|
70
src/test/java/org/qortal/test/network/RNSNetworkTest.java
Normal file
70
src/test/java/org/qortal/test/network/RNSNetworkTest.java
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package org.qortal.test.network;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
//import org.junit.Before;
|
||||||
|
//import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
//import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
//import java.util.Arrays;
|
||||||
|
|
||||||
|
import static io.reticulum.constant.ReticulumConstant.ETC_DIR;
|
||||||
|
import static org.apache.commons.lang3.SystemUtils.USER_HOME;
|
||||||
|
//import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
class ReticulumTest {
|
||||||
|
|
||||||
|
//@Test
|
||||||
|
//void t() throws DecoderException {
|
||||||
|
// System.out.println(Arrays.toString(Hex.decodeHex("adf54d882c9a9b80771eb4995d702d4a3e733391b2a0f53f416d9f907e55cff8")));
|
||||||
|
// System.out.println(2 + 1 + (128 / 8) * 2);
|
||||||
|
//}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void path() {
|
||||||
|
System.out.println(initConfig(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
//@Test
|
||||||
|
//void testConfigYamlParse() throws IOException {
|
||||||
|
// var config = ConfigObj.initConfig(Path.of(getSystemClassLoader().getResource("reticulum.default.yml").getPath()));
|
||||||
|
// assertNotNull(config);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//@Test
|
||||||
|
//void testHKDF() {
|
||||||
|
// var ifac_netname = "name";
|
||||||
|
// var ifac_netkey = "password";
|
||||||
|
// var ifacOrigin = new byte[]{};
|
||||||
|
// ifacOrigin = ArrayUtils.addAll(ifacOrigin, getSha256Digest().digest(ifac_netname.getBytes(UTF_8)));
|
||||||
|
// ifacOrigin = ArrayUtils.addAll(ifacOrigin, getSha256Digest().digest(ifac_netkey.getBytes(UTF_8)));
|
||||||
|
//
|
||||||
|
// var ifacOriginHash = getSha256Digest().digest(ifacOrigin);
|
||||||
|
//
|
||||||
|
// var HKDF = new HKDFBytesGenerator(new SHA256Digest());
|
||||||
|
// HKDF.init(new HKDFParameters(ifacOriginHash, IFAC_SALT, new byte[0]));
|
||||||
|
// var result = new byte[64];
|
||||||
|
// var len = HKDF.generateBytes(result, 0, result.length);
|
||||||
|
//
|
||||||
|
// assertNotNull(Hex.encodeHexString(result));
|
||||||
|
//}
|
||||||
|
|
||||||
|
private String initConfig(String configDir) {
|
||||||
|
if (StringUtils.isNotBlank(configDir)) {
|
||||||
|
return configDir;
|
||||||
|
} else {
|
||||||
|
if (Files.isDirectory(Path.of(ETC_DIR)) && Files.exists(Path.of(ETC_DIR, "config"))) {
|
||||||
|
return ETC_DIR;
|
||||||
|
} else if (
|
||||||
|
Files.isDirectory(Path.of(USER_HOME, ".config", "reticulum"))
|
||||||
|
&& Files.exists(Path.of(USER_HOME, ".config", "reticulum", "config"))
|
||||||
|
) {
|
||||||
|
return Path.of(USER_HOME, ".config", "reticulum").toString();
|
||||||
|
} else {
|
||||||
|
return Path.of(USER_HOME, ".reticulum").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
start.sh
5
start.sh
@ -34,7 +34,7 @@ fi
|
|||||||
# Comment out for bigger systems, e.g. non-routers
|
# Comment out for bigger systems, e.g. non-routers
|
||||||
# or when API documentation is enabled
|
# or when API documentation is enabled
|
||||||
# Uncomment (remove '#' sign) line below if your system has less than 12GB of RAM for optimal RAM defaults
|
# Uncomment (remove '#' sign) line below if your system has less than 12GB of RAM for optimal RAM defaults
|
||||||
JVM_MEMORY_ARGS="-Xss256m -XX:+UseSerialGC"
|
#JVM_MEMORY_ARGS="-Xss256m -XX:+UseSerialGC"
|
||||||
|
|
||||||
# Although java.net.preferIPv4Stack is supposed to be false
|
# Although java.net.preferIPv4Stack is supposed to be false
|
||||||
# by default in Java 11, on some platforms (e.g. FreeBSD 12),
|
# by default in Java 11, on some platforms (e.g. FreeBSD 12),
|
||||||
@ -43,6 +43,9 @@ JVM_MEMORY_ARGS="-Xss256m -XX:+UseSerialGC"
|
|||||||
nohup nice -n 20 java \
|
nohup nice -n 20 java \
|
||||||
-Djava.net.preferIPv4Stack=false \
|
-Djava.net.preferIPv4Stack=false \
|
||||||
${JVM_MEMORY_ARGS} \
|
${JVM_MEMORY_ARGS} \
|
||||||
|
--add-opens=java.base/java.lang=ALL-UNNAMED \
|
||||||
|
--add-opens=java.base/java.net=ALL-UNNAMED \
|
||||||
|
--illegal-access=warn \
|
||||||
-jar qortal.jar \
|
-jar qortal.jar \
|
||||||
1>run.log 2>&1 &
|
1>run.log 2>&1 &
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user