improve logging

This commit is contained in:
Manfred Karrer 2015-11-07 23:13:19 +01:00
parent 6e1e3d4be3
commit 13399057f7
29 changed files with 679 additions and 362 deletions

View file

@ -24,7 +24,9 @@ import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class Logging { public class Log {
public static boolean PRINT_TRACE_METHOD = true;
public static void setup(String fileName) { public static void setup(String fileName) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
@ -46,7 +48,7 @@ public class Logging {
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext); encoder.setContext(loggerContext);
encoder.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %xEx%n"); encoder.setPattern("%highlight(%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %xEx%n)");
encoder.start(); encoder.start();
appender.setEncoder(encoder); appender.setEncoder(encoder);
@ -57,4 +59,22 @@ public class Logging {
ch.qos.logback.classic.Logger logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); ch.qos.logback.classic.Logger logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
logbackLogger.addAppender(appender); logbackLogger.addAppender(appender);
} }
public static void traceCall() {
StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1];
String methodName = stackTraceElement.getMethodName();
if (methodName.equals("<init>"))
methodName = "Constructor ";
String className = stackTraceElement.getClassName();
LoggerFactory.getLogger(className).trace("Called: {}", methodName);
}
public static void traceCall(String message) {
StackTraceElement stackTraceElement = new Throwable().getStackTrace()[1];
String methodName = stackTraceElement.getMethodName();
if (methodName.equals("<init>"))
methodName = "Constructor ";
String className = stackTraceElement.getClassName();
LoggerFactory.getLogger(className).trace("Called: {} [{}]", methodName, message);
}
} }

View file

@ -35,6 +35,7 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URLConnection; import java.net.URLConnection;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -372,6 +373,10 @@ public class Utilities {
} }
} }
public static void setThreadName(String name) {
Thread.currentThread().setName(name + "-" + new Random().nextInt(10000));
}
private static class AnnotationExclusionStrategy implements ExclusionStrategy { private static class AnnotationExclusionStrategy implements ExclusionStrategy {
@Override @Override
public boolean shouldSkipField(FieldAttributes f) { public boolean shouldSkipField(FieldAttributes f) {

View file

@ -127,7 +127,7 @@ public class ArbitratorManager {
} }
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override

View file

@ -81,6 +81,7 @@ public class ArbitratorService {
} }
public Map<Address, Arbitrator> getArbitrators() { public Map<Address, Arbitrator> getArbitrators() {
// TODO java.lang.IllegalStateException: Duplicate key
final Map<Address, Arbitrator> arbitratorsMap = p2PService.getDataMap().values().stream() final Map<Address, Arbitrator> arbitratorsMap = p2PService.getDataMap().values().stream()
.filter(e -> e.expirablePayload instanceof Arbitrator) .filter(e -> e.expirablePayload instanceof Arbitrator)
.map(e -> (Arbitrator) e.expirablePayload) .map(e -> (Arbitrator) e.expirablePayload)

View file

@ -129,7 +129,7 @@ public class DisputeManager {
} }
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override

View file

@ -28,6 +28,7 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ExceptionHandler; import io.bitsquare.common.handlers.ExceptionHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import javafx.beans.property.*; import javafx.beans.property.*;
import org.bitcoinj.core.*; import org.bitcoinj.core.*;
@ -122,7 +123,7 @@ public class WalletService {
Timer timeoutTimer = FxTimer.runLater( Timer timeoutTimer = FxTimer.runLater(
Duration.ofMillis(STARTUP_TIMEOUT), Duration.ofMillis(STARTUP_TIMEOUT),
() -> { () -> {
Thread.currentThread().setName("WalletService:StartupTimeout-" + new Random().nextInt(1000)); Utilities.setThreadName("WalletService:StartupTimeout");
exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT / 1000 + " seconds.")); exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT / 1000 + " seconds."));
} }
); );

View file

@ -162,7 +162,7 @@ public class TradeManager {
} }
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override

View file

@ -18,6 +18,7 @@
package io.bitsquare.trade.offer; package io.bitsquare.trade.offer;
import io.bitsquare.app.Version; import io.bitsquare.app.Version;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Tradable; import io.bitsquare.trade.Tradable;
import io.bitsquare.trade.TradableList; import io.bitsquare.trade.TradableList;
@ -29,7 +30,6 @@ import org.slf4j.LoggerFactory;
import java.io.Serializable; import java.io.Serializable;
import java.time.Duration; import java.time.Duration;
import java.util.Date; import java.util.Date;
import java.util.Random;
public class OpenOffer implements Tradable, Serializable { public class OpenOffer implements Tradable, Serializable {
// That object is saved to disc. We need to take care of changes to not break deserialization. // That object is saved to disc. We need to take care of changes to not break deserialization.
@ -103,8 +103,8 @@ public class OpenOffer implements Tradable, Serializable {
timeoutTimer = FxTimer.runLater( timeoutTimer = FxTimer.runLater(
Duration.ofMillis(TIMEOUT), Duration.ofMillis(TIMEOUT),
() -> { () -> {
Thread.currentThread().setName("OpenOffer:Timeout-" + new Random().nextInt(1000)); Utilities.setThreadName("OpenOffer:Timeout");
log.debug("Timeout reached"); log.info("Timeout reached");
if (state == State.RESERVED) if (state == State.RESERVED)
setState(State.AVAILABLE); setState(State.AVAILABLE);
}); });

View file

@ -24,6 +24,7 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
@ -47,7 +48,6 @@ import javax.inject.Named;
import java.io.File; import java.io.File;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Random;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -142,7 +142,7 @@ public class OpenOfferManager {
} }
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override
@ -165,7 +165,7 @@ public class OpenOfferManager {
TimerTask timerTask = new TimerTask() { TimerTask timerTask = new TimerTask() {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setName("RepublishOffers-" + new Random().nextInt(1000)); Utilities.setThreadName("RepublishOffers");
UserThread.execute(() -> rePublishOffers()); UserThread.execute(() -> rePublishOffers());
try { try {
} catch (Throwable t) { } catch (Throwable t) {

View file

@ -20,6 +20,7 @@ package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.taskrunner.TaskRunner; import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.messaging.DecryptedMailListener; import io.bitsquare.p2p.messaging.DecryptedMailListener;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
@ -34,7 +35,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.Duration; import java.time.Duration;
import java.util.Random;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
@ -145,7 +145,7 @@ public class OfferAvailabilityProtocol {
stopTimeout(); stopTimeout();
timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> { timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> {
Thread.currentThread().setName("OfferAvailabilityProtocol:Timeout-" + new Random().nextInt(1000)); Utilities.setThreadName("OfferAvailabilityProtocol:Timeout");
log.warn("Timeout reached"); log.warn("Timeout reached");
errorMessageHandler.handleErrorMessage("Timeout reached: Peer has not responded."); errorMessageHandler.handleErrorMessage("Timeout reached: Peer has not responded.");
}); });

View file

@ -19,6 +19,7 @@ package io.bitsquare.trade.protocol.trade;
import io.bitsquare.arbitration.Arbitrator; import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.messaging.DecryptedMailListener; import io.bitsquare.p2p.messaging.DecryptedMailListener;
@ -36,7 +37,6 @@ import org.slf4j.LoggerFactory;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Random;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
@ -127,7 +127,7 @@ public abstract class TradeProtocol {
stopTimeout(); stopTimeout();
timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> { timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> {
Thread.currentThread().setName("TradeProtocol:Timeout-" + new Random().nextInt(1000)); Utilities.setThreadName("TradeProtocol:Timeout");
log.error("Timeout reached"); log.error("Timeout reached");
trade.setErrorMessage("A timeout occurred."); trade.setErrorMessage("A timeout occurred.");
cleanupTradable(); cleanupTradable();

View file

@ -100,7 +100,8 @@ public class BitsquareApp extends Application {
public void start(Stage primaryStage) throws IOException { public void start(Stage primaryStage) throws IOException {
BitsquareApp.primaryStage = primaryStage; BitsquareApp.primaryStage = primaryStage;
Logging.setup(Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "bitsquare").toString()); Log.setup(Paths.get(env.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY), "bitsquare").toString());
Log.PRINT_TRACE_METHOD = DEV_MODE;
UserThread.setExecutor(Platform::runLater); UserThread.setExecutor(Platform::runLater);

View file

@ -214,7 +214,7 @@ class MainViewModel implements ViewModel {
} }
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
p2pNetworkInfoFooter.set("Data received from peer."); p2pNetworkInfoFooter.set("Data received from peer.");
p2pNetworkReady.set(true); p2pNetworkReady.set(true);
} }

View file

@ -2,11 +2,11 @@
<configuration> <configuration>
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %xEx%n</pattern> <pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15} - %msg %xEx%n)</pattern>
</encoder> </encoder>
</appender> </appender>
<root level="INFO"> <root level="TRACE">
<appender-ref ref="CONSOLE_APPENDER"/> <appender-ref ref="CONSOLE_APPENDER"/>
</root> </root>
@ -20,7 +20,8 @@
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/> <logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/>
<logger name="io.bitsquare.btc.AddressBasedCoinSelector" level="OFF"/> <logger name="io.bitsquare.btc.AddressBasedCoinSelector" level="WARN"/>
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
<logger name="io.bitsquare.gui.util.Profiler" level="ERROR"/> <logger name="io.bitsquare.gui.util.Profiler" level="ERROR"/>
<logger name="io.bitsquare.locale.BSResources" level="ERROR"/> <logger name="io.bitsquare.locale.BSResources" level="ERROR"/>

View file

@ -5,8 +5,8 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import io.bitsquare.app.Log;
import io.bitsquare.app.ProgramArguments; import io.bitsquare.app.ProgramArguments;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.CryptoException; import io.bitsquare.common.crypto.CryptoException;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
@ -75,7 +75,7 @@ public class P2PService implements SetupListener {
private boolean shutDownComplete; private boolean shutDownComplete;
private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty(); private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
private final BooleanProperty allDataLoaded = new SimpleBooleanProperty(); private final BooleanProperty requestingDataCompleted = new SimpleBooleanProperty();
private final BooleanProperty authenticated = new SimpleBooleanProperty(); private final BooleanProperty authenticated = new SimpleBooleanProperty();
private MonadicBinding<Boolean> readyForAuthentication; private MonadicBinding<Boolean> readyForAuthentication;
@ -92,6 +92,7 @@ public class P2PService implements SetupListener {
@Nullable EncryptionService encryptionService, @Nullable EncryptionService encryptionService,
KeyRing keyRing, KeyRing keyRing,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
Log.traceCall();
this.seedNodesRepository = seedNodesRepository; this.seedNodesRepository = seedNodesRepository;
this.port = port; this.port = port;
this.torDir = torDir; this.torDir = torDir;
@ -106,6 +107,7 @@ public class P2PService implements SetupListener {
} }
private void init() { private void init() {
Log.traceCall();
// network // network
Set<Address> seedNodeAddresses; Set<Address> seedNodeAddresses;
if (useLocalhost) { if (useLocalhost) {
@ -127,32 +129,37 @@ public class P2PService implements SetupListener {
networkNode.addConnectionListener(new ConnectionListener() { networkNode.addConnectionListener(new ConnectionListener() {
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
Log.traceCall();
} }
@Override @Override
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) { public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
Log.traceCall();
checkArgument(peerAddress.equals(connection.getPeerAddress()), checkArgument(peerAddress.equals(connection.getPeerAddress()),
"peerAddress must match connection.getPeerAddress()"); "peerAddress must match connection.getPeerAddress()");
authenticatedPeerAddresses.add(peerAddress); authenticatedPeerAddresses.add(peerAddress);
authenticated.set(true); authenticated.set(true);
dataStorage.setAuthenticated(); dataStorage.setAuthenticated();
UserThread.execute(() -> p2pServiceListeners.stream().forEach(e -> e.onAuthenticated())); p2pServiceListeners.stream().forEach(e -> e.onAuthenticated());
} }
@Override @Override
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
Log.traceCall();
if (connection.isAuthenticated()) if (connection.isAuthenticated())
authenticatedPeerAddresses.remove(connection.getPeerAddress()); authenticatedPeerAddresses.remove(connection.getPeerAddress());
} }
@Override @Override
public void onError(Throwable throwable) { public void onError(Throwable throwable) {
Log.traceCall();
log.error("onError self/ConnectionException " + networkNode.getAddress() + "/" + throwable); log.error("onError self/ConnectionException " + networkNode.getAddress() + "/" + throwable);
} }
}); });
networkNode.addMessageListener((message, connection) -> { networkNode.addMessageListener((message, connection) -> {
Log.traceCall();
if (message instanceof GetDataRequest) { if (message instanceof GetDataRequest) {
log.trace("Received GetDataSetMessage: " + message); log.trace("Received GetDataSetMessage: " + message);
networkNode.sendMessage(connection, new GetDataResponse(getDataSet())); networkNode.sendMessage(connection, new GetDataResponse(getDataSet()));
@ -166,15 +173,15 @@ public class P2PService implements SetupListener {
} else { } else {
log.trace("Received DataSetMessage: Empty data set"); log.trace("Received DataSetMessage: Empty data set");
} }
allDataLoaded(); setRequestingDataCompleted();
} else if (message instanceof SealedAndSignedMessage) { } else if (message instanceof SealedAndSignedMessage) {
if (encryptionService != null) { if (encryptionService != null) {
try { try {
SealedAndSignedMessage sealedAndSignedMessage = (SealedAndSignedMessage) message; SealedAndSignedMessage sealedAndSignedMessage = (SealedAndSignedMessage) message;
DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify( DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify(
sealedAndSignedMessage.sealedAndSigned); sealedAndSignedMessage.sealedAndSigned);
UserThread.execute(() -> decryptedMailListeners.stream().forEach( decryptedMailListeners.stream().forEach(
e -> e.onMailMessage(decryptedMsgWithPubKey, connection.getPeerAddress()))); e -> e.onMailMessage(decryptedMsgWithPubKey, connection.getPeerAddress()));
} catch (CryptoException e) { } catch (CryptoException e) {
log.info("Decryption of SealedAndSignedMessage failed. " + log.info("Decryption of SealedAndSignedMessage failed. " +
"That is expected if the message is not intended for us."); "That is expected if the message is not intended for us.");
@ -186,6 +193,7 @@ public class P2PService implements SetupListener {
peerGroup.addPeerListener(new PeerListener() { peerGroup.addPeerListener(new PeerListener() {
@Override @Override
public void onFirstAuthenticatePeer(Peer peer) { public void onFirstAuthenticatePeer(Peer peer) {
Log.traceCall();
log.trace("onFirstAuthenticatePeer " + peer); log.trace("onFirstAuthenticatePeer " + peer);
sendGetAllDataMessageAfterAuthentication(peer); sendGetAllDataMessageAfterAuthentication(peer);
@ -193,41 +201,46 @@ public class P2PService implements SetupListener {
@Override @Override
public void onPeerAdded(Peer peer) { public void onPeerAdded(Peer peer) {
Log.traceCall();
} }
@Override @Override
public void onPeerRemoved(Address address) { public void onPeerRemoved(Address address) {
Log.traceCall();
} }
@Override @Override
public void onConnectionAuthenticated(Connection connection) { public void onConnectionAuthenticated(Connection connection) {
Log.traceCall();
} }
}); });
dataStorage.addHashMapChangedListener(new HashMapChangedListener() { dataStorage.addHashMapChangedListener(new HashMapChangedListener() {
@Override @Override
public void onAdded(ProtectedData entry) { public void onAdded(ProtectedData entry) {
Log.traceCall();
if (entry instanceof ProtectedMailboxData) if (entry instanceof ProtectedMailboxData)
tryDecryptMailboxData((ProtectedMailboxData) entry); tryDecryptMailboxData((ProtectedMailboxData) entry);
} }
@Override @Override
public void onRemoved(ProtectedData entry) { public void onRemoved(ProtectedData entry) {
Log.traceCall();
} }
}); });
readyForAuthentication = EasyBind.combine(hiddenServicePublished, allDataLoaded, authenticated, readyForAuthentication = EasyBind.combine(hiddenServicePublished, requestingDataCompleted, authenticated,
(a, b, c) -> a && b && !c); (a, b, c) -> a && b && !c);
readyForAuthentication.subscribe((observable, oldValue, newValue) -> { readyForAuthentication.subscribe((observable, oldValue, newValue) -> {
// we need to have both the initial data delivered and the hidden service published before we // we need to have both the initial data delivered and the hidden service published before we
// bootstrap and authenticate to other nodes. // bootstrap and authenticate to other nodes.
if (newValue) if (newValue)
authenticateSeedNode(); tryAuthenticateSeedNode();
}); });
allDataLoaded.addListener((observable, oldValue, newValue) -> { requestingDataCompleted.addListener((observable, oldValue, newValue) -> {
if (newValue) if (newValue)
UserThread.execute(() -> p2pServiceListeners.stream().forEach(e -> e.onAllDataReceived())); p2pServiceListeners.stream().forEach(e -> e.onRequestingDataCompleted());
}); });
} }
@ -238,18 +251,20 @@ public class P2PService implements SetupListener {
@Override @Override
public void onTorNodeReady() { public void onTorNodeReady() {
UserThread.execute(() -> p2pServiceListeners.stream().forEach(e -> e.onTorNodeReady())); Log.traceCall();
p2pServiceListeners.stream().forEach(e -> e.onTorNodeReady());
// 1. Step: As soon we have the tor node ready (hidden service still not available) we request the // 1. Step: As soon we have the tor node ready (hidden service still not available) we request the
// data set from a random seed node. // data set from a random seed node.
sendGetAllDataMessage(peerGroup.getSeedNodeAddresses()); sendGetDataRequest(peerGroup.getSeedNodeAddresses());
} }
@Override @Override
public void onHiddenServicePublished() { public void onHiddenServicePublished() {
Log.traceCall();
checkArgument(networkNode.getAddress() != null, "Address must be set when we have the hidden service ready"); checkArgument(networkNode.getAddress() != null, "Address must be set when we have the hidden service ready");
UserThread.execute(() -> p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished())); p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished());
// 3. (or 2.). Step: Hidden service is published // 3. (or 2.). Step: Hidden service is published
hiddenServicePublished.set(true); hiddenServicePublished.set(true);
@ -257,12 +272,13 @@ public class P2PService implements SetupListener {
@Override @Override
public void onSetupFailed(Throwable throwable) { public void onSetupFailed(Throwable throwable) {
UserThread.execute(() -> p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable))); Log.traceCall();
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
} }
private void sendGetAllDataMessage(Collection<Address> seedNodeAddresses) { private void sendGetDataRequest(Collection<Address> seedNodeAddresses) {
Log.traceCall();
if (!seedNodeAddresses.isEmpty()) { if (!seedNodeAddresses.isEmpty()) {
log.trace("sendGetAllDataMessage");
List<Address> remainingSeedNodeAddresses = new ArrayList<>(seedNodeAddresses); List<Address> remainingSeedNodeAddresses = new ArrayList<>(seedNodeAddresses);
Collections.shuffle(remainingSeedNodeAddresses); Collections.shuffle(remainingSeedNodeAddresses);
Address candidate = remainingSeedNodeAddresses.remove(0); Address candidate = remainingSeedNodeAddresses.remove(0);
@ -281,34 +297,39 @@ public class P2PService implements SetupListener {
log.info("Send GetAllDataMessage to " + candidate + " failed. " + log.info("Send GetAllDataMessage to " + candidate + " failed. " +
"That is expected if other seed nodes are offline." + "That is expected if other seed nodes are offline." +
"\nException:" + throwable.getMessage()); "\nException:" + throwable.getMessage());
if (!remainingSeedNodeAddresses.isEmpty())
log.trace("We try to connect another random seed node. " + remainingSeedNodeAddresses); log.trace("We try to connect another random seed node. " + remainingSeedNodeAddresses);
sendGetAllDataMessage(remainingSeedNodeAddresses);
sendGetDataRequest(remainingSeedNodeAddresses);
} }
}); });
} else { } else {
log.info("There is no seed node available for requesting data. That is expected for the first seed node."); log.info("There is no seed node available for requesting data. That is expected for the first seed node.");
allDataLoaded(); setRequestingDataCompleted();
} }
} }
private void allDataLoaded() { private void setRequestingDataCompleted() {
Log.traceCall();
// 2. (or 3.) Step: We got all data loaded // 2. (or 3.) Step: We got all data loaded
if (!allDataLoaded.get()) { if (!requestingDataCompleted.get())
log.trace("allDataLoaded"); requestingDataCompleted.set(true);
allDataLoaded.set(true);
}
} }
// 4. Step: hiddenServicePublished and allDataLoaded. We start authenticate to the connected seed node. // 4. Step: hiddenServicePublished and allDataLoaded. We start authenticate to the connected seed node.
private void authenticateSeedNode() { private void tryAuthenticateSeedNode() {
Log.traceCall();
if (connectedSeedNode != null) { if (connectedSeedNode != null) {
log.trace("authenticateSeedNode"); log.trace("authenticateSeedNode");
peerGroup.authenticateSeedNode(connectedSeedNode); peerGroup.authenticateSeedNode(connectedSeedNode);
} else {
log.debug("No connected seedNode available.");
} }
} }
// 5. Step: // 5. Step:
private void sendGetAllDataMessageAfterAuthentication(final Peer peer) { private void sendGetAllDataMessageAfterAuthentication(final Peer peer) {
Log.traceCall();
log.trace("sendGetDataSetMessageAfterAuthentication"); log.trace("sendGetDataSetMessageAfterAuthentication");
// After authentication we request again data as we might have missed pushed data in the meantime // After authentication we request again data as we might have missed pushed data in the meantime
SettableFuture<Connection> future = networkNode.sendMessage(peer.connection, new GetDataRequest()); SettableFuture<Connection> future = networkNode.sendMessage(peer.connection, new GetDataRequest());
@ -333,14 +354,17 @@ public class P2PService implements SetupListener {
// used by seed nodes to exclude themselves form list // used by seed nodes to exclude themselves form list
public void removeMySeedNodeAddressFromList(Address mySeedNodeAddress) { public void removeMySeedNodeAddressFromList(Address mySeedNodeAddress) {
Log.traceCall();
peerGroup.removeMySeedNodeAddressFromList(mySeedNodeAddress); peerGroup.removeMySeedNodeAddressFromList(mySeedNodeAddress);
} }
public void start() { public void start() {
Log.traceCall();
start(null); start(null);
} }
public void start(@Nullable P2PServiceListener listener) { public void start(@Nullable P2PServiceListener listener) {
Log.traceCall();
if (listener != null) if (listener != null)
addP2PServiceListener(listener); addP2PServiceListener(listener);
@ -348,6 +372,7 @@ public class P2PService implements SetupListener {
} }
public void shutDown(Runnable shutDownCompleteHandler) { public void shutDown(Runnable shutDownCompleteHandler) {
Log.traceCall();
if (!shutDownInProgress) { if (!shutDownInProgress) {
shutDownInProgress = true; shutDownInProgress = true;
@ -361,12 +386,12 @@ public class P2PService implements SetupListener {
if (networkNode != null) if (networkNode != null)
networkNode.shutDown(() -> { networkNode.shutDown(() -> {
UserThread.execute(() -> shutDownResultHandlers.stream().forEach(e -> new Thread(e).start())); shutDownResultHandlers.stream().forEach(e -> e.run());
shutDownComplete = true; shutDownComplete = true;
}); });
} else { } else {
if (shutDownComplete) if (shutDownComplete)
new Thread(shutDownCompleteHandler).start(); shutDownCompleteHandler.run();
else else
shutDownResultHandlers.add(shutDownCompleteHandler); shutDownResultHandlers.add(shutDownCompleteHandler);
log.warn("shutDown already in progress"); log.warn("shutDown already in progress");
@ -380,19 +405,21 @@ public class P2PService implements SetupListener {
public void sendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message, public void sendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message,
SendMailMessageListener sendMailMessageListener) { SendMailMessageListener sendMailMessageListener) {
Log.traceCall();
checkNotNull(peerAddress, "PeerAddress must not be null (sendEncryptedMailMessage)"); checkNotNull(peerAddress, "PeerAddress must not be null (sendEncryptedMailMessage)");
checkAuthentication(); checkAuthentication();
if (!authenticatedPeerAddresses.contains(peerAddress)) if (!authenticatedPeerAddresses.contains(peerAddress))
peerGroup.authenticateToPeer(peerAddress, peerGroup.authenticateToPeer(peerAddress,
() -> doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener), () -> doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener),
() -> UserThread.execute(() -> sendMailMessageListener.onFault())); () -> sendMailMessageListener.onFault());
else else
doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener); doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener);
} }
private void doSendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message, private void doSendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message,
SendMailMessageListener sendMailMessageListener) { SendMailMessageListener sendMailMessageListener) {
Log.traceCall();
if (encryptionService != null) { if (encryptionService != null) {
try { try {
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage( SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
@ -401,24 +428,25 @@ public class P2PService implements SetupListener {
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
UserThread.execute(() -> sendMailMessageListener.onArrived()); sendMailMessageListener.onArrived();
} }
@Override @Override
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
throwable.printStackTrace(); throwable.printStackTrace();
UserThread.execute(() -> sendMailMessageListener.onFault()); sendMailMessageListener.onFault();
} }
}); });
} catch (CryptoException e) { } catch (CryptoException e) {
e.printStackTrace(); e.printStackTrace();
UserThread.execute(() -> sendMailMessageListener.onFault()); sendMailMessageListener.onFault();
} }
} }
} }
public void sendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing, public void sendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
Log.traceCall();
checkNotNull(peerAddress, "PeerAddress must not be null (sendEncryptedMailboxMessage)"); checkNotNull(peerAddress, "PeerAddress must not be null (sendEncryptedMailboxMessage)");
checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer"); checkArgument(!keyRing.getPubKeyRing().equals(peersPubKeyRing), "We got own keyring instead of that from peer");
checkAuthentication(); checkAuthentication();
@ -437,6 +465,7 @@ public class P2PService implements SetupListener {
private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing, private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) { MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
Log.traceCall();
if (encryptionService != null) { if (encryptionService != null) {
try { try {
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage( SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
@ -446,7 +475,7 @@ public class P2PService implements SetupListener {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
log.trace("SendEncryptedMailboxMessage onSuccess"); log.trace("SendEncryptedMailboxMessage onSuccess");
UserThread.execute(() -> sendMailboxMessageListener.onArrived()); sendMailboxMessageListener.onArrived();
} }
@Override @Override
@ -460,13 +489,13 @@ public class P2PService implements SetupListener {
keyRing.getSignatureKeyPair().getPublic(), keyRing.getSignatureKeyPair().getPublic(),
receiverStoragePublicKey), receiverStoragePublicKey),
receiverStoragePublicKey); receiverStoragePublicKey);
UserThread.execute(() -> sendMailboxMessageListener.onStoredInMailbox()); sendMailboxMessageListener.onStoredInMailbox();
} }
}); });
} catch (CryptoException e) { } catch (CryptoException e) {
e.printStackTrace(); e.printStackTrace();
log.error("sendEncryptedMessage failed"); log.error("sendEncryptedMessage failed");
UserThread.execute(() -> sendMailboxMessageListener.onFault()); sendMailboxMessageListener.onFault();
} }
} }
} }
@ -477,6 +506,7 @@ public class P2PService implements SetupListener {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean addData(ExpirablePayload expirablePayload) { public boolean addData(ExpirablePayload expirablePayload) {
Log.traceCall();
checkAuthentication(); checkAuthentication();
try { try {
@ -489,6 +519,7 @@ public class P2PService implements SetupListener {
} }
private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) { private void addMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) {
Log.traceCall();
checkAuthentication(); checkAuthentication();
try { try {
@ -500,6 +531,7 @@ public class P2PService implements SetupListener {
} }
public boolean removeData(ExpirablePayload expirablePayload) { public boolean removeData(ExpirablePayload expirablePayload) {
Log.traceCall();
checkAuthentication(); checkAuthentication();
try { try {
@ -512,6 +544,7 @@ public class P2PService implements SetupListener {
} }
public void removeEntryFromMailbox(DecryptedMsgWithPubKey decryptedMsgWithPubKey) { public void removeEntryFromMailbox(DecryptedMsgWithPubKey decryptedMsgWithPubKey) {
Log.traceCall();
checkAuthentication(); checkAuthentication();
ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey); ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey);
@ -525,6 +558,7 @@ public class P2PService implements SetupListener {
} }
private void removeMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) { private void removeMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) {
Log.traceCall();
checkAuthentication(); checkAuthentication();
try { try {
@ -536,6 +570,7 @@ public class P2PService implements SetupListener {
} }
public Map<BigInteger, ProtectedData> getDataMap() { public Map<BigInteger, ProtectedData> getDataMap() {
Log.traceCall();
return dataStorage.getMap(); return dataStorage.getMap();
} }
@ -545,38 +580,47 @@ public class P2PService implements SetupListener {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void addMessageListener(MessageListener messageListener) { public void addMessageListener(MessageListener messageListener) {
Log.traceCall();
networkNode.addMessageListener(messageListener); networkNode.addMessageListener(messageListener);
} }
public void removeMessageListener(MessageListener messageListener) { public void removeMessageListener(MessageListener messageListener) {
Log.traceCall();
networkNode.removeMessageListener(messageListener); networkNode.removeMessageListener(messageListener);
} }
public void addDecryptedMailListener(DecryptedMailListener listener) { public void addDecryptedMailListener(DecryptedMailListener listener) {
Log.traceCall();
decryptedMailListeners.add(listener); decryptedMailListeners.add(listener);
} }
public void removeDecryptedMailListener(DecryptedMailListener listener) { public void removeDecryptedMailListener(DecryptedMailListener listener) {
Log.traceCall();
decryptedMailListeners.remove(listener); decryptedMailListeners.remove(listener);
} }
public void addDecryptedMailboxListener(DecryptedMailboxListener listener) { public void addDecryptedMailboxListener(DecryptedMailboxListener listener) {
Log.traceCall();
decryptedMailboxListeners.add(listener); decryptedMailboxListeners.add(listener);
} }
public void removeDecryptedMailboxListener(DecryptedMailboxListener listener) { public void removeDecryptedMailboxListener(DecryptedMailboxListener listener) {
Log.traceCall();
decryptedMailboxListeners.remove(listener); decryptedMailboxListeners.remove(listener);
} }
public void addP2PServiceListener(P2PServiceListener listener) { public void addP2PServiceListener(P2PServiceListener listener) {
Log.traceCall();
p2pServiceListeners.add(listener); p2pServiceListeners.add(listener);
} }
public void removeP2PServiceListener(P2PServiceListener listener) { public void removeP2PServiceListener(P2PServiceListener listener) {
Log.traceCall();
p2pServiceListeners.remove(listener); p2pServiceListeners.remove(listener);
} }
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) { public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
Log.traceCall();
dataStorage.addHashMapChangedListener(hashMapChangedListener); dataStorage.addHashMapChangedListener(hashMapChangedListener);
} }
@ -586,22 +630,27 @@ public class P2PService implements SetupListener {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean isAuthenticated() { public boolean isAuthenticated() {
Log.traceCall();
return authenticated.get(); return authenticated.get();
} }
public NetworkNode getNetworkNode() { public NetworkNode getNetworkNode() {
Log.traceCall();
return networkNode; return networkNode;
} }
public PeerGroup getPeerGroup() { public PeerGroup getPeerGroup() {
Log.traceCall();
return peerGroup; return peerGroup;
} }
public Address getAddress() { public Address getAddress() {
Log.traceCall();
return networkNode.getAddress(); return networkNode.getAddress();
} }
public NetworkStatistics getNetworkStatistics() { public NetworkStatistics getNetworkStatistics() {
Log.traceCall();
return networkStatistics; return networkStatistics;
} }
@ -611,10 +660,12 @@ public class P2PService implements SetupListener {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private HashSet<ProtectedData> getDataSet() { private HashSet<ProtectedData> getDataSet() {
Log.traceCall();
return new HashSet<>(dataStorage.getMap().values()); return new HashSet<>(dataStorage.getMap().values());
} }
private void tryDecryptMailboxData(ProtectedMailboxData mailboxData) { private void tryDecryptMailboxData(ProtectedMailboxData mailboxData) {
Log.traceCall();
if (encryptionService != null) { if (encryptionService != null) {
ExpirablePayload data = mailboxData.expirablePayload; ExpirablePayload data = mailboxData.expirablePayload;
if (data instanceof ExpirableMailboxPayload) { if (data instanceof ExpirableMailboxPayload) {
@ -627,12 +678,11 @@ public class P2PService implements SetupListener {
Address senderAddress = mailboxMessage.getSenderAddress(); Address senderAddress = mailboxMessage.getSenderAddress();
checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages"); checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages");
mailboxMap.put(decryptedMsgWithPubKey, mailboxData); mailboxMap.put(decryptedMsgWithPubKey, mailboxData);
log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress=" log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress="
+ senderAddress + " / my address=" + getAddress()); + senderAddress + " / my address=" + getAddress());
UserThread.execute(() -> decryptedMailboxListeners.stream().forEach( decryptedMailboxListeners.stream().forEach(
e -> e.onMailboxMessageAdded(decryptedMsgWithPubKey, senderAddress))); e -> e.onMailboxMessageAdded(decryptedMsgWithPubKey, senderAddress));
} }
} catch (CryptoException e) { } catch (CryptoException e) {
log.trace("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us. " + e.getMessage()); log.trace("Decryption of SealedAndSignedMessage failed. That is expected if the message is not intended for us. " + e.getMessage());
@ -642,6 +692,7 @@ public class P2PService implements SetupListener {
} }
private void checkAuthentication() { private void checkAuthentication() {
Log.traceCall();
if (authenticatedPeerAddresses.isEmpty()) if (authenticatedPeerAddresses.isEmpty())
throw new AuthenticationException("You must be authenticated before adding data to the P2P network."); throw new AuthenticationException("You must be authenticated before adding data to the P2P network.");
} }

View file

@ -5,7 +5,7 @@ import io.bitsquare.p2p.network.SetupListener;
public interface P2PServiceListener extends SetupListener { public interface P2PServiceListener extends SetupListener {
void onAllDataReceived(); void onRequestingDataCompleted();
void onAuthenticated(); void onAuthenticated();
} }

View file

@ -2,6 +2,7 @@ package io.bitsquare.p2p.network;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Uninterruptibles; import com.google.common.util.concurrent.Uninterruptibles;
import io.bitsquare.app.Log;
import io.bitsquare.common.ByteArrayUtils; import io.bitsquare.common.ByteArrayUtils;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
@ -24,7 +25,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* Connection is created by the server thread or by send message from NetworkNode. * Connection is created by the server thread or by sendMessage from NetworkNode.
* All handlers are called on User thread. * All handlers are called on User thread.
* Shared data between InputHandler thread and that * Shared data between InputHandler thread and that
*/ */
@ -34,7 +35,7 @@ public class Connection {
private static final int MAX_ILLEGAL_REQUESTS = 5; private static final int MAX_ILLEGAL_REQUESTS = 5;
private static final int SOCKET_TIMEOUT = 10 * 60 * 1000; // 10 min. //TODO set shorter private static final int SOCKET_TIMEOUT = 10 * 60 * 1000; // 10 min. //TODO set shorter
private InputHandler inputHandler; private InputHandler inputHandler;
private boolean isAuthenticated; private volatile boolean isAuthenticated;
public static int getMaxMsgSize() { public static int getMaxMsgSize() {
return MAX_MSG_SIZE; return MAX_MSG_SIZE;
@ -43,6 +44,7 @@ public class Connection {
private final String portInfo; private final String portInfo;
private final String uid; private final String uid;
private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
public final String objectId = super.toString().split("@")[1];
// set in init // set in init
private ObjectOutputStream objectOutputStream; private ObjectOutputStream objectOutputStream;
@ -66,6 +68,7 @@ public class Connection {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) { public Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) {
Log.traceCall();
portInfo = "localPort=" + socket.getLocalPort() + "/port=" + socket.getPort(); portInfo = "localPort=" + socket.getLocalPort() + "/port=" + socket.getPort();
uid = UUID.randomUUID().toString(); uid = UUID.randomUUID().toString();
@ -73,6 +76,7 @@ public class Connection {
} }
private void init(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) { private void init(Socket socket, MessageListener messageListener, ConnectionListener connectionListener) {
Log.traceCall();
sharedSpace = new SharedSpace(this, socket, messageListener, connectionListener, useCompression); sharedSpace = new SharedSpace(this, socket, messageListener, connectionListener, useCompression);
try { try {
socket.setSoTimeout(SOCKET_TIMEOUT); socket.setSoTimeout(SOCKET_TIMEOUT);
@ -102,17 +106,22 @@ public class Connection {
// API // API
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Called form UserThread
public void setAuthenticated(Address peerAddress, Connection connection) { public void setAuthenticated(Address peerAddress, Connection connection) {
Log.traceCall();
synchronized (peerAddress) {
this.peerAddress = peerAddress; this.peerAddress = peerAddress;
}
isAuthenticated = true; isAuthenticated = true;
UserThread.execute(() -> sharedSpace.getConnectionListener().onPeerAddressAuthenticated(peerAddress, connection)); sharedSpace.getConnectionListener().onPeerAddressAuthenticated(peerAddress, connection);
} }
// Called form various threads
public void sendMessage(Message message) { public void sendMessage(Message message) {
Log.traceCall();
if (!stopped) { if (!stopped) {
try { try {
log.trace("writeObject " + message + " on connection with port " + portInfo); log.info("writeObject " + message + " on connection with port " + portInfo);
if (!stopped) {
Object objectToWrite; Object objectToWrite;
if (useCompression) { if (useCompression) {
byte[] messageAsBytes = ByteArrayUtils.objectToByteArray(message); byte[] messageAsBytes = ByteArrayUtils.objectToByteArray(message);
@ -124,9 +133,11 @@ public class Connection {
// log.trace("Write object data size: " + ByteArrayUtils.objectToByteArray(message).length); // log.trace("Write object data size: " + ByteArrayUtils.objectToByteArray(message).length);
objectToWrite = message; objectToWrite = message;
} }
if (!stopped) {
synchronized (objectOutputStream) {
objectOutputStream.writeObject(objectToWrite); objectOutputStream.writeObject(objectToWrite);
objectOutputStream.flush(); objectOutputStream.flush();
}
sharedSpace.updateLastActivityDate(); sharedSpace.updateLastActivityDate();
} }
} catch (IOException e) { } catch (IOException e) {
@ -134,37 +145,47 @@ public class Connection {
sharedSpace.handleConnectionException(e); sharedSpace.handleConnectionException(e);
} }
} else { } else {
log.debug("sendMessage after stopped"); log.debug("called sendMessage but was already stopped");
} }
} }
public void reportIllegalRequest(IllegalRequest illegalRequest) { public void reportIllegalRequest(IllegalRequest illegalRequest) {
Log.traceCall();
sharedSpace.reportIllegalRequest(illegalRequest); sharedSpace.reportIllegalRequest(illegalRequest);
} }
public synchronized void setPeerAddress(@Nullable Address peerAddress) {
Log.traceCall();
this.peerAddress = peerAddress;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Nullable @Nullable
public Address getPeerAddress() { public synchronized Address getPeerAddress() {
//Log.traceCall();
return peerAddress; return peerAddress;
} }
public Date getLastActivityDate() { public Date getLastActivityDate() {
//Log.traceCall();
return sharedSpace.getLastActivityDate(); return sharedSpace.getLastActivityDate();
} }
public boolean isAuthenticated() { public boolean isAuthenticated() {
//Log.traceCall();
return isAuthenticated; return isAuthenticated;
} }
public String getUid() { public String getUid() {
Log.traceCall();
return uid; return uid;
} }
public boolean isStopped() { public boolean isStopped() {
Log.traceCall();
return stopped; return stopped;
} }
@ -174,26 +195,30 @@ public class Connection {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void shutDown(Runnable completeHandler) { public void shutDown(Runnable completeHandler) {
// Log.traceCall();
shutDown(true, completeHandler); shutDown(true, completeHandler);
} }
public void shutDown() { public void shutDown() {
//Log.traceCall();
shutDown(true, null); shutDown(true, null);
} }
private void shutDown(boolean sendCloseConnectionMessage) { private void shutDown(boolean sendCloseConnectionMessage) {
//Log.traceCall();
shutDown(sendCloseConnectionMessage, null); shutDown(sendCloseConnectionMessage, null);
} }
private void shutDown(boolean sendCloseConnectionMessage, @Nullable Runnable shutDownCompleteHandler) { private void shutDown(boolean sendCloseConnectionMessage, @Nullable Runnable shutDownCompleteHandler) {
Log.traceCall(this.toString());
if (!stopped) { if (!stopped) {
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"ShutDown connection:" "ShutDown connection:"
+ "\npeerAddress=" + peerAddress + "\npeerAddress=" + peerAddress
+ "\nlocalPort/port=" + sharedSpace.getSocket().getLocalPort() + "\nlocalPort/port=" + sharedSpace.getSocket().getLocalPort()
+ "/" + sharedSpace.getSocket().getPort() + "/" + sharedSpace.getSocket().getPort()
+ "\nobjectId=" + getObjectId() + " / uid=" + getUid() + "\nobjectId=" + objectId + " / uid=" + uid
+ "\nisAuthenticated=" + isAuthenticated() + "\nisAuthenticated=" + isAuthenticated
+ "\n############################################################\n"); + "\n############################################################\n");
log.trace("ShutDown connection requested. Connection=" + this.toString()); log.trace("ShutDown connection requested. Connection=" + this.toString());
@ -205,10 +230,9 @@ public class Connection {
if (sendCloseConnectionMessage) { if (sendCloseConnectionMessage) {
new Thread(() -> { new Thread(() -> {
Thread.currentThread().setName("Connection:SendCloseConnectionMessage-" + this.getObjectId()); Thread.currentThread().setName("Connection:SendCloseConnectionMessage-" + this.objectId);
try { try {
sendMessage(new CloseConnectionMessage()); sendMessage(new CloseConnectionMessage());
// give a bit of time for closing gracefully
Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
@ -224,10 +248,12 @@ public class Connection {
} }
private void continueShutDown(@Nullable Runnable shutDownCompleteHandler) { private void continueShutDown(@Nullable Runnable shutDownCompleteHandler) {
Log.traceCall();
ConnectionListener.Reason shutDownReason = sharedSpace.getShutDownReason(); ConnectionListener.Reason shutDownReason = sharedSpace.getShutDownReason();
if (shutDownReason == null) if (shutDownReason == null)
shutDownReason = ConnectionListener.Reason.SHUT_DOWN; shutDownReason = ConnectionListener.Reason.SHUT_DOWN;
final ConnectionListener.Reason finalShutDownReason = shutDownReason; final ConnectionListener.Reason finalShutDownReason = shutDownReason;
// keep UserThread.execute as its not clear if that is called from a non-UserThread
UserThread.execute(() -> sharedSpace.getConnectionListener().onDisconnect(finalShutDownReason, this)); UserThread.execute(() -> sharedSpace.getConnectionListener().onDisconnect(finalShutDownReason, this));
try { try {
@ -236,12 +262,12 @@ public class Connection {
log.trace("SocketException at shutdown might be expected " + e.getMessage()); log.trace("SocketException at shutdown might be expected " + e.getMessage());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
log.error("Exception at shutdown. " + e.getMessage());
} finally { } finally {
MoreExecutors.shutdownAndAwaitTermination(singleThreadExecutor, 500, TimeUnit.MILLISECONDS); MoreExecutors.shutdownAndAwaitTermination(singleThreadExecutor, 500, TimeUnit.MILLISECONDS);
log.debug("Connection shutdown complete " + this.toString()); log.debug("Connection shutdown complete " + this.toString());
// dont use executorService as its shut down but call handler on own thread // keep UserThread.execute as its not clear if that is called from a non-UserThread
// to not get interrupted by caller
if (shutDownCompleteHandler != null) if (shutDownCompleteHandler != null)
UserThread.execute(shutDownCompleteHandler); UserThread.execute(shutDownCompleteHandler);
} }
@ -273,7 +299,7 @@ public class Connection {
return "Connection{" + return "Connection{" +
"portInfo=" + portInfo + "portInfo=" + portInfo +
", uid='" + uid + '\'' + ", uid='" + uid + '\'' +
", objectId='" + getObjectId() + '\'' + ", objectId='" + objectId + '\'' +
", sharedSpace=" + sharedSpace.toString() + ", sharedSpace=" + sharedSpace.toString() +
", peerAddress=" + peerAddress + ", peerAddress=" + peerAddress +
", isAuthenticated=" + isAuthenticated + ", isAuthenticated=" + isAuthenticated +
@ -283,21 +309,13 @@ public class Connection {
'}'; '}';
} }
public String getObjectId() {
return super.toString().split("@")[1];
}
public void setPeerAddress(@Nullable Address peerAddress) {
this.peerAddress = peerAddress;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// SharedSpace // SharedSpace
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
/** /**
* Holds all shared data between Connection and InputHandler * Holds all shared data between Connection and InputHandler
* Runs in same thread as Connection
*/ */
private static class SharedSpace { private static class SharedSpace {
private static final Logger log = LoggerFactory.getLogger(SharedSpace.class); private static final Logger log = LoggerFactory.getLogger(SharedSpace.class);
@ -316,6 +334,7 @@ public class Connection {
public SharedSpace(Connection connection, Socket socket, MessageListener messageListener, public SharedSpace(Connection connection, Socket socket, MessageListener messageListener,
ConnectionListener connectionListener, boolean useCompression) { ConnectionListener connectionListener, boolean useCompression) {
Log.traceCall();
this.connection = connection; this.connection = connection;
this.socket = socket; this.socket = socket;
this.messageListener = messageListener; this.messageListener = messageListener;
@ -323,15 +342,18 @@ public class Connection {
this.useCompression = useCompression; this.useCompression = useCompression;
} }
public void updateLastActivityDate() { public synchronized void updateLastActivityDate() {
Log.traceCall();
lastActivityDate = new Date(); lastActivityDate = new Date();
} }
public Date getLastActivityDate() { public synchronized Date getLastActivityDate() {
// Log.traceCall();
return lastActivityDate; return lastActivityDate;
} }
public void reportIllegalRequest(IllegalRequest illegalRequest) { public void reportIllegalRequest(IllegalRequest illegalRequest) {
Log.traceCall();
log.warn("We got reported an illegal request " + illegalRequest); log.warn("We got reported an illegal request " + illegalRequest);
int prevCounter = illegalRequests.get(illegalRequest); int prevCounter = illegalRequests.get(illegalRequest);
if (prevCounter > illegalRequest.maxTolerance) { if (prevCounter > illegalRequest.maxTolerance) {
@ -343,6 +365,7 @@ public class Connection {
} }
public void handleConnectionException(Exception e) { public void handleConnectionException(Exception e) {
Log.traceCall();
if (e instanceof SocketException) { if (e instanceof SocketException) {
if (socket.isClosed()) if (socket.isClosed())
shutDownReason = ConnectionListener.Reason.SOCKET_CLOSED; shutDownReason = ConnectionListener.Reason.SOCKET_CLOSED;
@ -358,32 +381,50 @@ public class Connection {
e.printStackTrace(); e.printStackTrace();
} }
if (!stopped) if (!stopped) {
stopped = true;
connection.shutDown(false); connection.shutDown(false);
} }
}
public void onMessage(Message message) { public void onMessage(Message message) {
Log.traceCall();
UserThread.execute(() -> messageListener.onMessage(message, connection)); UserThread.execute(() -> messageListener.onMessage(message, connection));
} }
public boolean useCompression() { public boolean useCompression() {
Log.traceCall();
return useCompression; return useCompression;
} }
public void shutDown(boolean sendCloseConnectionMessage) { public void shutDown(boolean sendCloseConnectionMessage) {
Log.traceCall();
connection.shutDown(sendCloseConnectionMessage); connection.shutDown(sendCloseConnectionMessage);
} }
public ConnectionListener getConnectionListener() { public synchronized ConnectionListener getConnectionListener() {
Log.traceCall();
return connectionListener; return connectionListener;
} }
public Socket getSocket() { public synchronized Socket getSocket() {
//Log.traceCall();
return socket; return socket;
} }
public String getConnectionId() { public String getConnectionId() {
return connection.getObjectId(); Log.traceCall();
return connection.objectId;
}
public void stop() {
Log.traceCall();
this.stopped = true;
}
public synchronized ConnectionListener.Reason getShutDownReason() {
Log.traceCall();
return shutDownReason;
} }
@Override @Override
@ -396,13 +437,6 @@ public class Connection {
'}'; '}';
} }
public void stop() {
this.stopped = true;
}
public ConnectionListener.Reason getShutDownReason() {
return shutDownReason;
}
} }
@ -410,6 +444,7 @@ public class Connection {
// InputHandler // InputHandler
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Runs in same thread as Connection
private static class InputHandler implements Runnable { private static class InputHandler implements Runnable {
private static final Logger log = LoggerFactory.getLogger(InputHandler.class); private static final Logger log = LoggerFactory.getLogger(InputHandler.class);
@ -419,24 +454,27 @@ public class Connection {
private volatile boolean stopped; private volatile boolean stopped;
public InputHandler(SharedSpace sharedSpace, ObjectInputStream objectInputStream, String portInfo) { public InputHandler(SharedSpace sharedSpace, ObjectInputStream objectInputStream, String portInfo) {
Log.traceCall();
this.sharedSpace = sharedSpace; this.sharedSpace = sharedSpace;
this.objectInputStream = objectInputStream; this.objectInputStream = objectInputStream;
this.portInfo = portInfo; this.portInfo = portInfo;
} }
public void stop() { public void stop() {
Log.traceCall();
stopped = true; stopped = true;
} }
@Override @Override
public void run() { public void run() {
Log.traceCall();
try { try {
Thread.currentThread().setName("InputHandler-" + portInfo); Thread.currentThread().setName("InputHandler-" + portInfo);
while (!stopped && !Thread.currentThread().isInterrupted()) { while (!stopped && !Thread.currentThread().isInterrupted()) {
try { try {
log.trace("InputHandler waiting for incoming messages connection=" + sharedSpace.getConnectionId()); log.trace("InputHandler waiting for incoming messages connection=" + sharedSpace.getConnectionId());
Object rawInputObject = objectInputStream.readObject(); Object rawInputObject = objectInputStream.readObject();
log.trace("New data arrived at inputHandler of connection=" + sharedSpace.getConnectionId() log.info("New data arrived at inputHandler of connection=" + sharedSpace.getConnectionId()
+ " rawInputObject " + rawInputObject); + " rawInputObject " + rawInputObject);
int size = ByteArrayUtils.objectToByteArray(rawInputObject).length; int size = ByteArrayUtils.objectToByteArray(rawInputObject).length;
@ -468,18 +506,16 @@ public class Connection {
if (message instanceof CloseConnectionMessage) { if (message instanceof CloseConnectionMessage) {
stopped = true; stopped = true;
sharedSpace.shutDown(false); sharedSpace.shutDown(false);
} else { } else if (!stopped) {
sharedSpace.onMessage(message); sharedSpace.onMessage(message);
} }
} else { } else {
sharedSpace.reportIllegalRequest(IllegalRequest.InvalidDataType); sharedSpace.reportIllegalRequest(IllegalRequest.InvalidDataType);
} }
} else { } else {
log.error("Received decompressed data exceeds max. msg size.");
sharedSpace.reportIllegalRequest(IllegalRequest.MaxSizeExceeded); sharedSpace.reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
} }
} else { } else {
log.error("Received compressed data exceeds max. msg size.");
sharedSpace.reportIllegalRequest(IllegalRequest.MaxSizeExceeded); sharedSpace.reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
} }
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
@ -489,7 +525,8 @@ public class Connection {
} }
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
log.error("Executing task failed. " + t.getMessage()); stopped = true;
sharedSpace.handleConnectionException(new Exception(t));
} }
} }

View file

@ -6,7 +6,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles; import com.google.common.util.concurrent.Uninterruptibles;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.nucleo.net.HiddenServiceDescriptor; import io.nucleo.net.HiddenServiceDescriptor;
import io.nucleo.net.TorNode; import io.nucleo.net.TorNode;
@ -18,15 +20,15 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
// Run in UserThread
public class LocalhostNetworkNode extends NetworkNode { public class LocalhostNetworkNode extends NetworkNode {
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNode.class); private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNode.class);
private static int simulateTorDelayTorNode = 1 * 100; private static volatile int simulateTorDelayTorNode = 1 * 100;
private static int simulateTorDelayHiddenService = 1 * 100; private static volatile int simulateTorDelayHiddenService = 1 * 100;
private Address address; private Address address;
public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) { public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) {
@ -44,28 +46,32 @@ public class LocalhostNetworkNode extends NetworkNode {
public LocalhostNetworkNode(int port) { public LocalhostNetworkNode(int port) {
super(port); super(port);
Log.traceCall();
} }
@Override @Override
public void start(@Nullable SetupListener setupListener) { public void start(@Nullable SetupListener setupListener) {
Log.traceCall();
if (setupListener != null) addSetupListener(setupListener); if (setupListener != null) addSetupListener(setupListener);
createExecutor(); createExecutorService();
//Tor delay simulation //Tor delay simulation
createTorNode(torNode -> { createTorNode(torNode -> {
Log.traceCall("torNode created");
setupListeners.stream().forEach(e -> e.onTorNodeReady()); setupListeners.stream().forEach(e -> e.onTorNodeReady());
// Create Hidden Service (takes about 40 sec.) // Create Hidden Service (takes about 40 sec.)
createHiddenService(hiddenServiceDescriptor -> { createHiddenService(hiddenServiceDescriptor -> {
Log.traceCall("hiddenService created");
try { try {
startServer(new ServerSocket(port)); startServer(new ServerSocket(servicePort));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
log.error("Exception at startServer: " + e.getMessage());
} }
address = new Address("localhost", port); address = new Address("localhost", servicePort);
setupListeners.stream().forEach(e -> e.onHiddenServicePublished()); setupListeners.stream().forEach(e -> e.onHiddenServicePublished());
}); });
@ -76,21 +82,26 @@ public class LocalhostNetworkNode extends NetworkNode {
@Override @Override
@Nullable @Nullable
public Address getAddress() { public Address getAddress() {
Log.traceCall();
return address; return address;
} }
// Called from NetworkNode thread
@Override @Override
protected Socket getSocket(Address peerAddress) throws IOException { protected Socket createSocket(Address peerAddress) throws IOException {
Log.traceCall();
return new Socket(peerAddress.hostName, peerAddress.port); return new Socket(peerAddress.hostName, peerAddress.port);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Tor delay simulation // Tor delay simulation
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createTorNode(final Consumer<TorNode> resultHandler) { private void createTorNode(final Consumer<TorNode> resultHandler) {
Log.traceCall();
ListenableFuture<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> future = executorService.submit(() -> { ListenableFuture<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:CreateTorNode-" + new Random().nextInt(1000)); Utilities.setThreadName("NetworkNode:CreateTorNode");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
if (simulateTorDelayTorNode > 0) if (simulateTorDelayTorNode > 0)
@ -100,6 +111,7 @@ public class LocalhostNetworkNode extends NetworkNode {
"TorNode created [simulation]:" + "TorNode created [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms" "\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n"); + "\n############################################################\n");
// as we are simulating we return null
return null; return null;
} catch (Throwable t) { } catch (Throwable t) {
throw t; throw t;
@ -107,18 +119,25 @@ public class LocalhostNetworkNode extends NetworkNode {
}); });
Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() { Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() {
public void onSuccess(TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode) { public void onSuccess(TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode) {
UserThread.execute(() -> resultHandler.accept(torNode)); UserThread.execute(() -> {
// as we are simulating we return null
resultHandler.accept(null);
});
} }
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
log.error("[simulation] TorNode creation failed"); UserThread.execute(() -> {
log.error("[simulation] TorNode creation failed. " + throwable.getMessage());
throwable.printStackTrace();
});
} }
}); });
} }
private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) { private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) {
Log.traceCall();
ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> { ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:CreateHiddenService-" + new Random().nextInt(1000)); Utilities.setThreadName("NetworkNode:CreateHiddenService");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
if (simulateTorDelayHiddenService > 0) if (simulateTorDelayHiddenService > 0)
@ -128,6 +147,7 @@ public class LocalhostNetworkNode extends NetworkNode {
"Hidden service created [simulation]:" + "Hidden service created [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms" "\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n"); + "\n############################################################\n");
// as we are simulating we return null
return null; return null;
} catch (Throwable t) { } catch (Throwable t) {
throw t; throw t;
@ -135,13 +155,18 @@ public class LocalhostNetworkNode extends NetworkNode {
}); });
Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() { Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() {
public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) { public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) {
UserThread.execute(() -> resultHandler.accept(hiddenServiceDescriptor)); UserThread.execute(() -> {
} // as we are simulating we return null
resultHandler.accept(null);
public void onFailure(@NotNull Throwable throwable) {
log.error("[simulation] Hidden service creation failed");
}
}); });
} }
public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> {
log.error("[simulation] Hidden service creation failed. " + throwable.getMessage());
throwable.printStackTrace();
});
}
});
}
} }

View file

@ -1,6 +1,7 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
@ -21,11 +22,12 @@ import java.util.concurrent.CopyOnWriteArraySet;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
// Run in UserThread
public abstract class NetworkNode implements MessageListener, ConnectionListener { public abstract class NetworkNode implements MessageListener, ConnectionListener {
private static final Logger log = LoggerFactory.getLogger(NetworkNode.class); private static final Logger log = LoggerFactory.getLogger(NetworkNode.class);
protected final int port; protected final int servicePort;
private final CopyOnWriteArraySet<Connection> outBoundConnections = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<Connection> inBoundConnections = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<Connection> inBoundConnections = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<MessageListener> messageListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<MessageListener> messageListeners = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<ConnectionListener> connectionListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<ConnectionListener> connectionListeners = new CopyOnWriteArraySet<>();
@ -34,13 +36,17 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
private Server server; private Server server;
private volatile boolean shutDownInProgress; private volatile boolean shutDownInProgress;
// accessed from different threads
private final CopyOnWriteArraySet<Connection> outBoundConnections = new CopyOnWriteArraySet<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public NetworkNode(int port) { public NetworkNode(int servicePort) {
this.port = port; Log.traceCall();
this.servicePort = servicePort;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -48,16 +54,17 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void start() { public void start() {
Log.traceCall();
start(null); start(null);
} }
abstract public void start(@Nullable SetupListener setupListener); abstract public void start(@Nullable SetupListener setupListener);
public SettableFuture<Connection> sendMessage(@NotNull Address peerAddress, Message message) { public SettableFuture<Connection> sendMessage(@NotNull Address peerAddress, Message message) {
log.trace("sendMessage message=" + message); Log.traceCall("message: " + message + " to peerAddress: " + peerAddress);
checkNotNull(peerAddress, "peerAddress must not be null"); checkNotNull(peerAddress, "peerAddress must not be null");
Optional<Connection> outboundConnectionOptional = findOutboundConnection(peerAddress); Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peerAddress);
Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null; Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null;
if (connection != null) if (connection != null)
log.trace("We have found a connection in outBoundConnections. Connection.uid=" + connection.getUid()); log.trace("We have found a connection in outBoundConnections. Connection.uid=" + connection.getUid());
@ -69,7 +76,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
} }
if (connection == null) { if (connection == null) {
Optional<Connection> inboundConnectionOptional = findInboundConnection(peerAddress); Optional<Connection> inboundConnectionOptional = lookupInboundConnection(peerAddress);
if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get(); if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get();
if (connection != null) if (connection != null)
log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid()); log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid());
@ -78,28 +85,28 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
if (connection != null) { if (connection != null) {
return sendMessage(connection, message); return sendMessage(connection, message);
} else { } else {
final SettableFuture<Connection> resultFuture = SettableFuture.create();
ListenableFuture<Connection> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:SendMessage-create-new-outbound-connection-to-" + peerAddress);
try {
Connection newConnection;
log.trace("We have not found any connection for that peerAddress. " + log.trace("We have not found any connection for that peerAddress. " +
"We will create a new outbound connection."); "We will create a new outbound connection.");
Socket socket = getSocket(peerAddress); // can take a while when using tor
newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this); final SettableFuture<Connection> resultFuture = SettableFuture.create();
ListenableFuture<Connection> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peerAddress);
try {
Socket socket = createSocket(peerAddress); // can take a while when using tor
Connection newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this);
newConnection.setPeerAddress(peerAddress); newConnection.setPeerAddress(peerAddress);
outBoundConnections.add(newConnection); outBoundConnections.add(newConnection);
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"NetworkNode created new outbound connection:" "NetworkNode created new outbound connection:"
+ "\npeerAddress=" + peerAddress.getFullAddress() + "\npeerAddress=" + peerAddress
+ "\nconnection.uid=" + newConnection.getUid() + "\nconnection.uid=" + newConnection.getUid()
+ "\nmessage=" + message + "\nmessage=" + message
+ "\n############################################################\n"); + "\n############################################################\n");
newConnection.sendMessage(message); newConnection.sendMessage(message);
return newConnection; return newConnection; // can take a while when using tor
} catch (Throwable throwable) { } catch (Throwable throwable) {
if (!(throwable instanceof ConnectException || throwable instanceof IOException)) { if (!(throwable instanceof ConnectException || throwable instanceof IOException)) {
throwable.printStackTrace(); throwable.printStackTrace();
@ -110,11 +117,15 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
}); });
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
UserThread.execute(() -> resultFuture.set(connection)); UserThread.execute(() -> {
resultFuture.set(connection);
});
} }
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> resultFuture.setException(throwable)); UserThread.execute(() -> {
resultFuture.setException(throwable);
});
} }
}); });
return resultFuture; return resultFuture;
@ -122,9 +133,10 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
} }
public SettableFuture<Connection> sendMessage(Connection connection, Message message) { public SettableFuture<Connection> sendMessage(Connection connection, Message message) {
Log.traceCall();
// connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block // connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block
ListenableFuture<Connection> future = executorService.submit(() -> { ListenableFuture<Connection> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:SendMessage-to-connection-" + connection.getObjectId()); Thread.currentThread().setName("NetworkNode:SendMessage-to-" + connection.objectId);
try { try {
log.debug("## connection.sendMessage"); log.debug("## connection.sendMessage");
connection.sendMessage(message); connection.sendMessage(message);
@ -136,25 +148,29 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
final SettableFuture<Connection> resultFuture = SettableFuture.create(); final SettableFuture<Connection> resultFuture = SettableFuture.create();
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
log.debug("## connection.sendMessage onSuccess"); UserThread.execute(() -> {
UserThread.execute(() -> resultFuture.set(connection)); resultFuture.set(connection);
});
} }
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
log.debug("## connection.sendMessage onFailure"); UserThread.execute(() -> {
UserThread.execute(() -> resultFuture.setException(throwable)); resultFuture.setException(throwable);
});
} }
}); });
return resultFuture; return resultFuture;
} }
public Set<Connection> getAllConnections() { public Set<Connection> getAllConnections() {
Log.traceCall();
Set<Connection> set = new HashSet<>(inBoundConnections); Set<Connection> set = new HashSet<>(inBoundConnections);
set.addAll(outBoundConnections); set.addAll(outBoundConnections);
return set; return set;
} }
public void shutDown(Runnable shutDownCompleteHandler) { public void shutDown(Runnable shutDownCompleteHandler) {
Log.traceCall();
log.info("Shutdown NetworkNode"); log.info("Shutdown NetworkNode");
if (!shutDownInProgress) { if (!shutDownInProgress) {
shutDownInProgress = true; shutDownInProgress = true;
@ -166,7 +182,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
getAllConnections().stream().forEach(e -> e.shutDown()); getAllConnections().stream().forEach(e -> e.shutDown());
log.info("NetworkNode shutdown complete"); log.info("NetworkNode shutdown complete");
if (shutDownCompleteHandler != null) UserThread.execute(() -> shutDownCompleteHandler.run()); if (shutDownCompleteHandler != null) shutDownCompleteHandler.run();
} }
} }
@ -176,6 +192,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void addSetupListener(SetupListener setupListener) { public void addSetupListener(SetupListener setupListener) {
Log.traceCall();
setupListeners.add(setupListener); setupListeners.add(setupListener);
} }
@ -185,38 +202,43 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void addConnectionListener(ConnectionListener connectionListener) { public void addConnectionListener(ConnectionListener connectionListener) {
Log.traceCall();
connectionListeners.add(connectionListener); connectionListeners.add(connectionListener);
} }
public void removeConnectionListener(ConnectionListener connectionListener) { public void removeConnectionListener(ConnectionListener connectionListener) {
Log.traceCall();
connectionListeners.remove(connectionListener); connectionListeners.remove(connectionListener);
} }
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
connectionListeners.stream().forEach(e -> UserThread.execute(() -> e.onConnection(connection))); Log.traceCall();
connectionListeners.stream().forEach(e -> e.onConnection(connection));
} }
@Override @Override
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) { public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
log.trace("onAuthenticationComplete peerAddress=" + peerAddress); Log.traceCall();
log.trace("onAuthenticationComplete connection=" + connection); log.trace("onAuthenticationComplete peerAddress/connection: " + peerAddress + " / " + connection);
connectionListeners.stream().forEach(e -> UserThread.execute(() -> e.onPeerAddressAuthenticated(peerAddress, connection))); connectionListeners.stream().forEach(e -> e.onPeerAddressAuthenticated(peerAddress, connection));
} }
@Override @Override
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
Log.traceCall();
Address peerAddress = connection.getPeerAddress(); Address peerAddress = connection.getPeerAddress();
log.trace("onDisconnect connection " + connection + ", peerAddress= " + peerAddress); log.trace("onDisconnect connection " + connection + ", peerAddress= " + peerAddress);
outBoundConnections.remove(connection); outBoundConnections.remove(connection);
inBoundConnections.remove(connection); inBoundConnections.remove(connection);
connectionListeners.stream().forEach(e -> UserThread.execute(() -> e.onDisconnect(reason, connection))); connectionListeners.stream().forEach(e -> e.onDisconnect(reason, connection));
} }
@Override @Override
public void onError(Throwable throwable) { public void onError(Throwable throwable) {
connectionListeners.stream().forEach(e -> UserThread.execute(() -> e.onError(throwable))); Log.traceCall();
connectionListeners.stream().forEach(e -> e.onError(throwable));
} }
@ -225,16 +247,19 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void addMessageListener(MessageListener messageListener) { public void addMessageListener(MessageListener messageListener) {
Log.traceCall();
messageListeners.add(messageListener); messageListeners.add(messageListener);
} }
public void removeMessageListener(MessageListener messageListener) { public void removeMessageListener(MessageListener messageListener) {
Log.traceCall();
messageListeners.remove(messageListener); messageListeners.remove(messageListener);
} }
@Override @Override
public void onMessage(Message message, Connection connection) { public void onMessage(Message message, Connection connection) {
messageListeners.stream().forEach(e -> UserThread.execute(() -> e.onMessage(message, connection))); Log.traceCall();
messageListeners.stream().forEach(e -> e.onMessage(message, connection));
} }
@ -242,16 +267,19 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
// Protected // Protected
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
protected void createExecutor() { protected void createExecutorService() {
executorService = Utilities.getListeningExecutorService("NetworkNode-" + port, 20, 50, 120L); Log.traceCall();
executorService = Utilities.getListeningExecutorService("NetworkNode-" + servicePort, 20, 50, 120L);
} }
protected void startServer(ServerSocket serverSocket) { protected void startServer(ServerSocket serverSocket) {
Log.traceCall();
server = new Server(serverSocket, server = new Server(serverSocket,
(message, connection) -> NetworkNode.this.onMessage(message, connection), (message, connection) -> NetworkNode.this.onMessage(message, connection),
new ConnectionListener() { new ConnectionListener() {
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
Log.traceCall();
// we still have not authenticated so put it to the temp list // we still have not authenticated so put it to the temp list
inBoundConnections.add(connection); inBoundConnections.add(connection);
NetworkNode.this.onConnection(connection); NetworkNode.this.onConnection(connection);
@ -259,36 +287,42 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
@Override @Override
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) { public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
Log.traceCall();
NetworkNode.this.onPeerAddressAuthenticated(peerAddress, connection); NetworkNode.this.onPeerAddressAuthenticated(peerAddress, connection);
} }
@Override @Override
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
Log.traceCall();
Address peerAddress = connection.getPeerAddress(); Address peerAddress = connection.getPeerAddress();
log.trace("onDisconnect at incoming connection to peerAddress " + peerAddress); log.trace("onDisconnect at incoming connection to peerAddress (or connection) "
+ ((peerAddress == null) ? connection : peerAddress));
inBoundConnections.remove(connection); inBoundConnections.remove(connection);
NetworkNode.this.onDisconnect(reason, connection); NetworkNode.this.onDisconnect(reason, connection);
} }
@Override @Override
public void onError(Throwable throwable) { public void onError(Throwable throwable) {
Log.traceCall();
NetworkNode.this.onError(throwable); NetworkNode.this.onError(throwable);
} }
}); });
executorService.submit(server); executorService.submit(server);
} }
private Optional<Connection> findOutboundConnection(Address peerAddress) { private Optional<Connection> lookupOutboundConnection(Address peerAddress) {
Log.traceCall();
return outBoundConnections.stream() return outBoundConnections.stream()
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny(); .filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
} }
private Optional<Connection> findInboundConnection(Address peerAddress) { private Optional<Connection> lookupInboundConnection(Address peerAddress) {
Log.traceCall();
return inBoundConnections.stream() return inBoundConnections.stream()
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny(); .filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
} }
abstract protected Socket getSocket(Address peerAddress) throws IOException; abstract protected Socket createSocket(Address peerAddress) throws IOException;
@Nullable @Nullable
abstract public Address getAddress(); abstract public Address getAddress();

View file

@ -1,5 +1,6 @@
package io.bitsquare.p2p.network; package io.bitsquare.p2p.network;
import io.bitsquare.app.Log;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -7,20 +8,24 @@ import java.io.IOException;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
// Runs in UserThread
class Server implements Runnable { class Server implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Server.class); private static final Logger log = LoggerFactory.getLogger(Server.class);
private final ServerSocket serverSocket;
private final MessageListener messageListener; private final MessageListener messageListener;
private final ConnectionListener connectionListener; private final ConnectionListener connectionListener;
private final Set<Connection> connections = new HashSet<>();
// accessed from different threads
private final ServerSocket serverSocket;
private final Set<Connection> connections = new CopyOnWriteArraySet<>();
private volatile boolean stopped; private volatile boolean stopped;
public Server(ServerSocket serverSocket, MessageListener messageListener, ConnectionListener connectionListener) { public Server(ServerSocket serverSocket, MessageListener messageListener, ConnectionListener connectionListener) {
Log.traceCall();
this.serverSocket = serverSocket; this.serverSocket = serverSocket;
this.messageListener = messageListener; this.messageListener = messageListener;
this.connectionListener = connectionListener; this.connectionListener = connectionListener;
@ -28,14 +33,15 @@ class Server implements Runnable {
@Override @Override
public void run() { public void run() {
Log.traceCall();
try { try {
// Thread created by NetworkNode // Thread created by NetworkNode
Thread.currentThread().setName("NetworkNode:Server-" + serverSocket.getLocalPort()); Thread.currentThread().setName("Server-" + serverSocket.getLocalPort());
try { try {
while (!stopped && !Thread.currentThread().isInterrupted()) { while (!stopped && !Thread.currentThread().isInterrupted()) {
log.info("Ready to accept new clients on port " + serverSocket.getLocalPort()); log.info("Ready to accept new clients on port " + serverSocket.getLocalPort());
final Socket socket = serverSocket.accept(); final Socket socket = serverSocket.accept();
if (!stopped) { if (!stopped && !Thread.currentThread().isInterrupted()) {
log.info("Accepted new client on localPort/port " + socket.getLocalPort() + "/" + socket.getPort()); log.info("Accepted new client on localPort/port " + socket.getLocalPort() + "/" + socket.getPort());
Connection connection = new Connection(socket, messageListener, connectionListener); Connection connection = new Connection(socket, messageListener, connectionListener);
@ -61,6 +67,7 @@ class Server implements Runnable {
} }
public void shutDown() { public void shutDown() {
Log.traceCall();
if (!stopped) { if (!stopped) {
stopped = true; stopped = true;

View file

@ -6,7 +6,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager; import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.Utils; import io.bitsquare.p2p.Utils;
import io.nucleo.net.HiddenServiceDescriptor; import io.nucleo.net.HiddenServiceDescriptor;
@ -19,29 +21,24 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.Random;
import java.util.Timer; import java.util.Timer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
// Run in UserThread
public class TorNetworkNode extends NetworkNode { public class TorNetworkNode extends NetworkNode {
private static final Logger log = LoggerFactory.getLogger(TorNetworkNode.class); private static final Logger log = LoggerFactory.getLogger(TorNetworkNode.class);
private static final Random random = new Random();
private static final long TIMEOUT = 5000;
private static final int MAX_ERRORS_BEFORE_RESTART = 3;
private static final int MAX_RESTART_ATTEMPTS = 3; private static final int MAX_RESTART_ATTEMPTS = 3;
private static final int WAIT_BEFORE_RESTART = 2000; private static final int WAIT_BEFORE_RESTART = 2000;
private static final long SHUT_DOWN_TIMEOUT = 5000; private static final long SHUT_DOWN_TIMEOUT = 5000;
private final File torDir; private final File torDir;
private TorNode torNode; private TorNode torNetworkNode;
private HiddenServiceDescriptor hiddenServiceDescriptor; private HiddenServiceDescriptor hiddenServiceDescriptor;
private Timer shutDownTimeoutTimer; private Timer shutDownTimeoutTimer;
private long nonce;
private int errorCounter;
private int restartCounter; private int restartCounter;
private Runnable shutDownCompleteHandler; private Runnable shutDownCompleteHandler;
private boolean torShutDownComplete, networkNodeShutDownDoneComplete; private boolean torShutDownComplete, networkNodeShutDownDoneComplete;
@ -53,7 +50,7 @@ public class TorNetworkNode extends NetworkNode {
public TorNetworkNode(int servicePort, File torDir) { public TorNetworkNode(int servicePort, File torDir) {
super(servicePort); super(servicePort);
Log.traceCall();
this.torDir = torDir; this.torDir = torDir;
} }
@ -64,24 +61,31 @@ public class TorNetworkNode extends NetworkNode {
@Override @Override
public void start(@Nullable SetupListener setupListener) { public void start(@Nullable SetupListener setupListener) {
Log.traceCall();
if (setupListener != null) if (setupListener != null)
addSetupListener(setupListener); addSetupListener(setupListener);
createExecutor(); createExecutorService();
// Create the tor node (takes about 6 sec.) // Create the tor node (takes about 6 sec.)
createTorNode(torDir, torNode -> { createTorNode(torDir, torNode -> {
TorNetworkNode.this.torNode = torNode; Log.traceCall("torNode created");
TorNetworkNode.this.torNetworkNode = torNode;
setupListeners.stream().forEach(e -> UserThread.execute(() -> e.onTorNodeReady())); setupListeners.stream().forEach(e -> e.onTorNodeReady());
// Create Hidden Service (takes about 40 sec.) // Create Hidden Service (takes about 40 sec.)
createHiddenService(torNode, Utils.findFreeSystemPort(), port, hiddenServiceDescriptor -> { createHiddenService(torNode,
Utils.findFreeSystemPort(),
servicePort,
hiddenServiceDescriptor -> {
Log.traceCall("hiddenService created");
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor; TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
startServer(hiddenServiceDescriptor.getServerSocket()); startServer(hiddenServiceDescriptor.getServerSocket());
UserThread.runAfter(() -> setupListeners.stream().forEach(e -> e.onHiddenServicePublished()), setupListeners.stream().forEach(e -> e.onHiddenServicePublished());
500, TimeUnit.MILLISECONDS); /* UserThread.runAfter(() -> setupListeners.stream().forEach(e -> e.onHiddenServicePublished()),
500, TimeUnit.MILLISECONDS);*/
}); });
}); });
} }
@ -89,14 +93,24 @@ public class TorNetworkNode extends NetworkNode {
@Override @Override
@Nullable @Nullable
public Address getAddress() { public Address getAddress() {
Log.traceCall();
if (hiddenServiceDescriptor != null) if (hiddenServiceDescriptor != null)
return new Address(hiddenServiceDescriptor.getFullAddress()); return new Address(hiddenServiceDescriptor.getFullAddress());
else else
return null; return null;
} }
@Override
protected Socket createSocket(Address peerAddress) throws IOException {
Log.traceCall();
checkArgument(peerAddress.hostName.endsWith(".onion"), "PeerAddress is not an onion address");
return torNetworkNode.connectToHiddenService(peerAddress.hostName, peerAddress.port);
}
//TODO simplify
public void shutDown(Runnable shutDownCompleteHandler) { public void shutDown(Runnable shutDownCompleteHandler) {
log.info("Shutdown TorNetworkNode"); Log.traceCall();
this.shutDownCompleteHandler = shutDownCompleteHandler; this.shutDownCompleteHandler = shutDownCompleteHandler;
shutDownTimeoutTimer = UserThread.runAfter(() -> { shutDownTimeoutTimer = UserThread.runAfter(() -> {
@ -105,41 +119,41 @@ public class TorNetworkNode extends NetworkNode {
}, SHUT_DOWN_TIMEOUT, TimeUnit.MILLISECONDS); }, SHUT_DOWN_TIMEOUT, TimeUnit.MILLISECONDS);
if (executorService != null) { if (executorService != null) {
executorService.submit(() -> super.shutDown(() -> { executorService.submit(() -> {
Utilities.setThreadName("TorNetworkNodeShutDownSuperClass");
UserThread.execute(() -> {
// We want to stay in UserThread
super.shutDown(() -> {
networkNodeShutDownDoneComplete = true; networkNodeShutDownDoneComplete = true;
if (torShutDownComplete) if (torShutDownComplete)
shutDownExecutorService(); shutDownExecutorService();
} });
)); });
});
} else { } else {
log.error("executorService must not be null at shutDown"); log.error("executorService must not be null at shutDown");
} }
ListenableFuture<?> future2 = executorService.submit(() -> { executorService.submit(() -> {
Utilities.setThreadName("NetworkNode:torNodeShutdown");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.info("Shutdown torNode"); log.info("Shutdown torNode");
if (torNode != null) // Might take a bit so we use a thread
torNode.shutdown(); if (torNetworkNode != null)
torNetworkNode.shutdown();
log.info("Shutdown torNode done after " + (System.currentTimeMillis() - ts) + " ms."); log.info("Shutdown torNode done after " + (System.currentTimeMillis() - ts) + " ms.");
} catch (Throwable e) { UserThread.execute(() -> {
e.printStackTrace();
log.error("Shutdown torNode failed with exception: " + e.getMessage());
shutDownExecutorService();
}
});
Futures.addCallback(future2, new FutureCallback<Object>() {
@Override
public void onSuccess(Object o) {
torShutDownComplete = true; torShutDownComplete = true;
if (networkNodeShutDownDoneComplete) if (networkNodeShutDownDoneComplete)
shutDownExecutorService(); shutDownExecutorService();
} });
} catch (Throwable e) {
@Override UserThread.execute(() -> {
public void onFailure(@NotNull Throwable throwable) { e.printStackTrace();
throwable.printStackTrace(); log.error("Shutdown torNode failed with exception: " + e.getMessage());
log.error("Shutdown torNode failed with exception: " + throwable.getMessage()); // We want to switch to UserThread
shutDownExecutorService(); shutDownExecutorService();
});
} }
}); });
} }
@ -149,26 +163,27 @@ public class TorNetworkNode extends NetworkNode {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void shutDownExecutorService() { private void shutDownExecutorService() {
Log.traceCall();
shutDownTimeoutTimer.cancel(); shutDownTimeoutTimer.cancel();
new Thread(() -> { new Thread(() -> {
Thread.currentThread().setName("NetworkNode:shutDownExecutorService-" + new Random().nextInt(1000)); Utilities.setThreadName("NetworkNode:shutDownExecutorService");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.info("Shutdown executorService"); log.debug("Shutdown executorService");
MoreExecutors.shutdownAndAwaitTermination(executorService, 500, TimeUnit.MILLISECONDS); MoreExecutors.shutdownAndAwaitTermination(executorService, 500, TimeUnit.MILLISECONDS);
log.info("Shutdown executorService done after " + (System.currentTimeMillis() - ts) + " ms."); log.debug("Shutdown executorService done after " + (System.currentTimeMillis() - ts) + " ms.");
log.info("Shutdown completed"); log.info("Shutdown completed");
UserThread.execute(() -> shutDownCompleteHandler.run()); shutDownCompleteHandler.run();
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
log.error("Shutdown executorService failed with exception: " + t.getMessage()); log.error("Shutdown executorService failed with exception: " + t.getMessage());
UserThread.execute(() -> shutDownCompleteHandler.run()); shutDownCompleteHandler.run();
} }
}).start(); }).start();
} }
private void restartTor() { private void restartTor() {
Log.traceCall();
restartCounter++; restartCounter++;
if (restartCounter <= MAX_RESTART_ATTEMPTS) { if (restartCounter <= MAX_RESTART_ATTEMPTS) {
shutDown(() -> UserThread.runAfter(() -> { shutDown(() -> UserThread.runAfter(() -> {
@ -189,8 +204,9 @@ public class TorNetworkNode extends NetworkNode {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createTorNode(final File torDir, final Consumer<TorNode> resultHandler) { private void createTorNode(final File torDir, final Consumer<TorNode> resultHandler) {
Log.traceCall();
ListenableFuture<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> future = executorService.submit(() -> { ListenableFuture<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:CreateTorNode-" + new Random().nextInt(1000)); Utilities.setThreadName("TorNetworkNode:CreateTorNode");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
if (torDir.mkdirs()) if (torDir.mkdirs())
@ -198,34 +214,41 @@ public class TorNetworkNode extends NetworkNode {
log.info("TorDir = " + torDir.getAbsolutePath()); log.info("TorDir = " + torDir.getAbsolutePath());
log.trace("Create TorNode"); log.trace("Create TorNode");
TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode1 = new TorNode<JavaOnionProxyManager, JavaOnionProxyContext>( TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode =
torDir) { new TorNode<JavaOnionProxyManager, JavaOnionProxyContext>(torDir) {
}; };
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"TorNode created:" + "TorNode created:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms" "\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n"); + "\n############################################################\n");
return torNode1; return torNode;
} catch (Throwable t) { } catch (Throwable t) {
throw t; throw t;
} }
}); });
Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() { Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() {
public void onSuccess(TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode) { public void onSuccess(TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode) {
Log.traceCall();
UserThread.execute(() -> {
resultHandler.accept(torNode); resultHandler.accept(torNode);
});
} }
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
Log.traceCall();
UserThread.execute(() -> {
log.error("TorNode creation failed with exception: " + throwable.getMessage()); log.error("TorNode creation failed with exception: " + throwable.getMessage());
restartTor(); restartTor();
});
} }
}); });
} }
private void createHiddenService(TorNode torNode, int localPort, int servicePort, private void createHiddenService(TorNode torNode, int localPort, int servicePort,
Consumer<HiddenServiceDescriptor> resultHandler) { Consumer<HiddenServiceDescriptor> resultHandler) {
Log.traceCall();
ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> { ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> {
Thread.currentThread().setName("NetworkNode:CreateHiddenService-" + new Random().nextInt(1000)); Utilities.setThreadName("TorNetworkNode:CreateHiddenService");
try { try {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
log.debug("Create hidden service"); log.debug("Create hidden service");
@ -243,23 +266,17 @@ public class TorNetworkNode extends NetworkNode {
}); });
Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() { Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() {
public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) { public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) {
UserThread.execute(() -> {
resultHandler.accept(hiddenServiceDescriptor); resultHandler.accept(hiddenServiceDescriptor);
}
public void onFailure(@NotNull Throwable throwable) {
log.error("Hidden service creation failed");
restartTor();
}
}); });
} }
public void onFailure(@NotNull Throwable throwable) {
@Override UserThread.execute(() -> {
protected Socket getSocket(Address peerAddress) throws IOException { log.error("Hidden service creation failed");
checkArgument(peerAddress.hostName.endsWith(".onion"), "PeerAddress is not an onion address"); restartTor();
});
return torNode.connectToHiddenService(peerAddress.hostName, peerAddress.port); }
});
} }
} }

View file

@ -4,7 +4,7 @@ import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.common.UserThread; import io.bitsquare.app.Log;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
@ -17,7 +17,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
// authentication example: // authentication example:
@ -43,6 +42,7 @@ public class AuthenticationHandshake {
private MessageListener messageListener; private MessageListener messageListener;
public AuthenticationHandshake(NetworkNode networkNode, PeerGroup peerGroup, Address myAddress) { public AuthenticationHandshake(NetworkNode networkNode, PeerGroup peerGroup, Address myAddress) {
Log.traceCall();
this.networkNode = networkNode; this.networkNode = networkNode;
this.peerGroup = peerGroup; this.peerGroup = peerGroup;
this.myAddress = myAddress; this.myAddress = myAddress;
@ -51,21 +51,25 @@ public class AuthenticationHandshake {
} }
private void onFault(@NotNull Throwable throwable) { private void onFault(@NotNull Throwable throwable) {
Log.traceCall();
cleanup(); cleanup();
UserThread.execute(() -> resultFuture.setException(throwable)); resultFuture.setException(throwable);
} }
private void onSuccess(Connection connection) { private void onSuccess(Connection connection) {
Log.traceCall();
cleanup(); cleanup();
UserThread.execute(() -> resultFuture.set(connection)); resultFuture.set(connection);
} }
private void cleanup() { private void cleanup() {
Log.traceCall();
stopped = true; stopped = true;
networkNode.removeMessageListener(messageListener); networkNode.removeMessageListener(messageListener);
} }
public SettableFuture<Connection> requestAuthenticationToPeer(Address peerAddress) { public SettableFuture<Connection> requestAuthenticationToPeer(Address peerAddress) {
Log.traceCall();
// Requesting peer // Requesting peer
resultFuture = SettableFuture.create(); resultFuture = SettableFuture.create();
startAuthTs = System.currentTimeMillis(); startAuthTs = System.currentTimeMillis();
@ -88,6 +92,7 @@ public class AuthenticationHandshake {
} }
public SettableFuture<Connection> requestAuthentication(Set<Address> remainingAddresses, Address peerAddress) { public SettableFuture<Connection> requestAuthentication(Set<Address> remainingAddresses, Address peerAddress) {
Log.traceCall();
// Requesting peer // Requesting peer
resultFuture = SettableFuture.create(); resultFuture = SettableFuture.create();
startAuthTs = System.currentTimeMillis(); startAuthTs = System.currentTimeMillis();
@ -113,6 +118,7 @@ public class AuthenticationHandshake {
} }
public SettableFuture<Connection> processAuthenticationRequest(AuthenticationRequest authenticationRequest, Connection connection) { public SettableFuture<Connection> processAuthenticationRequest(AuthenticationRequest authenticationRequest, Connection connection) {
Log.traceCall();
// Responding peer // Responding peer
resultFuture = SettableFuture.create(); resultFuture = SettableFuture.create();
startAuthTs = System.currentTimeMillis(); startAuthTs = System.currentTimeMillis();
@ -121,7 +127,10 @@ public class AuthenticationHandshake {
log.trace("RequestAuthenticationMessage from " + peerAddress + " at " + myAddress); log.trace("RequestAuthenticationMessage from " + peerAddress + " at " + myAddress);
log.info("We shut down inbound connection from peer {} to establish a new " + log.info("We shut down inbound connection from peer {} to establish a new " +
"connection with his reported address.", peerAddress); "connection with his reported address.", peerAddress);
connection.shutDown(() -> UserThread.runAfter(() -> {
//TODO check if causes problems without delay
connection.shutDown(() -> {
Log.traceCall();
if (!stopped) { if (!stopped) {
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to // we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections) // inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
@ -141,15 +150,38 @@ public class AuthenticationHandshake {
} }
}); });
} }
});
/* connection.shutDown(() -> UserThread.runAfter(() -> { Log.traceCall();
if (!stopped) {
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + myAddress);
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new AuthenticationResponse(myAddress, authenticationRequest.nonce, getAndSetNonce()));
Futures.addCallback(future, new FutureCallback<Connection>() {
@Override
public void onSuccess(Connection connection) { Log.traceCall();
log.trace("onSuccess sending ChallengeMessage");
}
@Override
public void onFailure(@NotNull Throwable throwable) { Log.traceCall();
log.warn("onFailure sending ChallengeMessage.");
onFault(throwable);
}
});
}
}, },
100 + PeerGroup.simulateAuthTorNode, 100 + PeerGroup.simulateAuthTorNode,
TimeUnit.MILLISECONDS)); TimeUnit.MILLISECONDS));*/
return resultFuture; return resultFuture;
} }
private void setupMessageListener() { private void setupMessageListener() {
Log.traceCall();
messageListener = (message, connection) -> { messageListener = (message, connection) -> {
Log.traceCall();
if (message instanceof AuthenticationMessage) { if (message instanceof AuthenticationMessage) {
if (message instanceof AuthenticationResponse) { if (message instanceof AuthenticationResponse) {
// Requesting peer // Requesting peer
@ -207,7 +239,7 @@ public class AuthenticationHandshake {
}); });
log.info("\n\nAuthenticationComplete: Peer with address " + peerAddress log.info("\n\nAuthenticationComplete: Peer with address " + peerAddress
+ " authenticated (" + connection.getObjectId() + "). Took " + " authenticated (" + connection.objectId + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n"); + (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
onSuccess(connection); onSuccess(connection);
@ -227,7 +259,7 @@ public class AuthenticationHandshake {
// we wait until the handshake is completed before setting the authenticate flag // we wait until the handshake is completed before setting the authenticate flag
// authentication at both sides of the connection // authentication at both sides of the connection
log.info("\n\nAuthenticationComplete\nPeer with address " + peerAddress log.info("\n\nAuthenticationComplete\nPeer with address " + peerAddress
+ " authenticated (" + connection.getObjectId() + "). Took " + " authenticated (" + connection.objectId + "). Took "
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n"); + (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
onSuccess(connection); onSuccess(connection);
@ -239,6 +271,7 @@ public class AuthenticationHandshake {
} }
private void authenticateToNextRandomPeer(Set<Address> remainingAddresses) { private void authenticateToNextRandomPeer(Set<Address> remainingAddresses) {
Log.traceCall();
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomAddressAndRemainingSet(remainingAddresses); Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomAddressAndRemainingSet(remainingAddresses);
if (tupleOptional.isPresent()) { if (tupleOptional.isPresent()) {
Tuple2<Address, Set<Address>> tuple = tupleOptional.get(); Tuple2<Address, Set<Address>> tuple = tupleOptional.get();
@ -250,6 +283,7 @@ public class AuthenticationHandshake {
} }
private Optional<Tuple2<Address, Set<Address>>> getRandomAddressAndRemainingSet(Set<Address> addresses) { private Optional<Tuple2<Address, Set<Address>>> getRandomAddressAndRemainingSet(Set<Address> addresses) {
Log.traceCall();
if (!addresses.isEmpty()) { if (!addresses.isEmpty()) {
List<Address> list = new ArrayList<>(addresses); List<Address> list = new ArrayList<>(addresses);
Collections.shuffle(list); Collections.shuffle(list);
@ -261,6 +295,7 @@ public class AuthenticationHandshake {
} }
private long getAndSetNonce() { private long getAndSetNonce() {
Log.traceCall();
nonce = new Random().nextLong(); nonce = new Random().nextLong();
while (nonce == 0) while (nonce == 0)
nonce = getAndSetNonce(); nonce = getAndSetNonce();

View file

@ -3,8 +3,10 @@ package io.bitsquare.p2p.peers;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.ConnectionListener; import io.bitsquare.p2p.network.ConnectionListener;
@ -19,14 +21,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
// Run in UserThread
public class PeerGroup { public class PeerGroup {
private static final Logger log = LoggerFactory.getLogger(PeerGroup.class); private static final Logger log = LoggerFactory.getLogger(PeerGroup.class);
@ -42,21 +42,21 @@ public class PeerGroup {
MAX_CONNECTIONS = maxConnections; MAX_CONNECTIONS = maxConnections;
} }
private static final int MAINTENANCE_INTERVAL = new Random().nextInt(2 * 60 * 1000) + 2 * 60 * 1000; // 2-4 min. private static final int SEND_PING_INTERVAL = 100000000;// new Random().nextInt(2 * 60 * 1000) + 2 * 60 * 1000; // 2-4 min.
private static final int GET_PEERS_INTERVAL = new Random().nextInt(1 * 60 * 1000) + 1 * 60 * 1000; // 1-2 min. private static final int GET_PEERS_INTERVAL = 100000000;//new Random().nextInt(1 * 60 * 1000) + 1 * 60 * 1000; // 1-2 min.
private static final int PING_AFTER_CONNECTION_INACTIVITY = 30 * 1000; private static final int PING_AFTER_CONNECTION_INACTIVITY = 30 * 1000;
private static final int MAX_REPORTED_PEERS = 1000;
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final Set<Address> seedNodeAddresses; private final Set<Address> seedNodeAddresses;
private final CopyOnWriteArraySet<PeerListener> peerListeners = new CopyOnWriteArraySet<>(); private final Set<PeerListener> peerListeners = new HashSet<>();
private final ConcurrentHashMap<Address, Peer> authenticatedPeers = new ConcurrentHashMap<>(); private final Map<Address, Peer> authenticatedPeers = new HashMap<>();
private final CopyOnWriteArraySet<Address> reportedPeerAddresses = new CopyOnWriteArraySet<>(); private final Set<Address> reportedPeerAddresses = new HashSet<>();
private final Timer maintenanceTimer = new Timer(); private final Timer sendPingTimer = new Timer();
private final Timer getPeersTimer = new Timer(); private final Timer getPeersTimer = new Timer();
private volatile boolean shutDownInProgress; private boolean shutDownInProgress;
private boolean firstPeerAdded = false; private boolean firstPeerAdded = false;
@ -65,6 +65,8 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public PeerGroup(NetworkNode networkNode, Set<Address> seeds) { public PeerGroup(NetworkNode networkNode, Set<Address> seeds) {
Log.traceCall();
this.networkNode = networkNode; this.networkNode = networkNode;
this.seedNodeAddresses = seeds; this.seedNodeAddresses = seeds;
@ -72,6 +74,7 @@ public class PeerGroup {
} }
private void init(NetworkNode networkNode) { private void init(NetworkNode networkNode) {
Log.traceCall();
networkNode.addMessageListener((message, connection) -> { networkNode.addMessageListener((message, connection) -> {
if (message instanceof MaintenanceMessage) if (message instanceof MaintenanceMessage)
processMaintenanceMessage((MaintenanceMessage) message, connection); processMaintenanceMessage((MaintenanceMessage) message, connection);
@ -83,16 +86,18 @@ public class PeerGroup {
networkNode.addConnectionListener(new ConnectionListener() { networkNode.addConnectionListener(new ConnectionListener() {
@Override @Override
public void onConnection(Connection connection) { public void onConnection(Connection connection) {
Log.traceCall();
} }
@Override @Override
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) { public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
Log.traceCall();
} }
@Override @Override
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
Log.traceCall();
log.debug("onDisconnect connection=" + connection + " / reason=" + reason); log.debug("onDisconnect connection=" + connection + " / reason=" + reason);
log.debug("##### onDisconnect connection.isAuthenticated()=" + connection.isAuthenticated());
// only removes authenticated nodes // only removes authenticated nodes
if (connection.isAuthenticated()) if (connection.isAuthenticated())
removePeer(connection.getPeerAddress()); removePeer(connection.getPeerAddress());
@ -100,6 +105,7 @@ public class PeerGroup {
@Override @Override
public void onError(Throwable throwable) { public void onError(Throwable throwable) {
Log.traceCall();
} }
}); });
@ -112,21 +118,23 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void removeMySeedNodeAddressFromList(Address mySeedNodeAddress) { public void removeMySeedNodeAddressFromList(Address mySeedNodeAddress) {
Log.traceCall();
seedNodeAddresses.remove(mySeedNodeAddress); seedNodeAddresses.remove(mySeedNodeAddress);
} }
public void shutDown() { public void shutDown() {
Log.traceCall();
if (!shutDownInProgress) { if (!shutDownInProgress) {
shutDownInProgress = true; shutDownInProgress = true;
if (maintenanceTimer != null) if (sendPingTimer != null)
maintenanceTimer.cancel(); sendPingTimer.cancel();
} }
} }
public void broadcast(BroadcastMessage message, @Nullable Address sender) { public void broadcast(BroadcastMessage message, @Nullable Address sender) {
Log.traceCall();
log.trace("Broadcast message to " + authenticatedPeers.values().size() + " peers."); log.trace("Broadcast message to " + authenticatedPeers.values().size() + " peers.");
log.trace("message = " + message); log.trace("message = " + message);
printAuthenticatedPeers();
// TODO add randomized timing? // TODO add randomized timing?
authenticatedPeers.values().stream() authenticatedPeers.values().stream()
@ -155,16 +163,15 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void processAuthenticationRequest(NetworkNode networkNode, AuthenticationRequest message, final Connection connection) { private void processAuthenticationRequest(NetworkNode networkNode, AuthenticationRequest message, final Connection connection) {
Log.traceCall();
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, PeerGroup.this, getMyAddress()); AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, PeerGroup.this, getMyAddress());
SettableFuture<Connection> future = authenticationHandshake.processAuthenticationRequest(message, connection); SettableFuture<Connection> future = authenticationHandshake.processAuthenticationRequest(message, connection);
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
public void onSuccess(@Nullable Connection connection) { public void onSuccess(@Nullable Connection connection) {
if (connection != null) { if (connection != null) {
UserThread.execute(() -> {
setAuthenticated(connection, connection.getPeerAddress()); setAuthenticated(connection, connection.getPeerAddress());
purgeReportedPeers(); purgeReportedPeersIfExceeds();
});
} }
} }
@ -172,12 +179,13 @@ public class PeerGroup {
public void onFailure(@NotNull Throwable throwable) { public void onFailure(@NotNull Throwable throwable) {
throwable.printStackTrace(); throwable.printStackTrace();
log.error("AuthenticationHandshake failed. " + throwable.getMessage()); log.error("AuthenticationHandshake failed. " + throwable.getMessage());
UserThread.execute(() -> removePeer(connection.getPeerAddress())); removePeer(connection.getPeerAddress());
} }
}); });
} }
public void authenticateSeedNode(Address peerAddress) { public void authenticateSeedNode(Address peerAddress) {
Log.traceCall();
authenticateToSeedNode(new HashSet<>(seedNodeAddresses), peerAddress, true); authenticateToSeedNode(new HashSet<>(seedNodeAddresses), peerAddress, true);
} }
@ -185,6 +193,7 @@ public class PeerGroup {
// After connection is authenticated, we try to connect to any reported peer as long we have not // After connection is authenticated, we try to connect to any reported peer as long we have not
// reached our max connection size. // reached our max connection size.
private void authenticateToSeedNode(Set<Address> remainingAddresses, Address peerAddress, boolean continueOnSuccess) { private void authenticateToSeedNode(Set<Address> remainingAddresses, Address peerAddress, boolean continueOnSuccess) {
Log.traceCall();
checkArgument(!authenticatedPeers.containsKey(peerAddress), checkArgument(!authenticatedPeers.containsKey(peerAddress),
"We have that peer already authenticated. That must never happen."); "We have that peer already authenticated. That must never happen.");
@ -232,6 +241,7 @@ public class PeerGroup {
} }
private void authenticateToRemainingReportedPeers() { private void authenticateToRemainingReportedPeers() {
Log.traceCall();
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(reportedPeerAddresses); Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(reportedPeerAddresses);
if (tupleOptional.isPresent()) { if (tupleOptional.isPresent()) {
log.info("We try to authenticate to a random peer. " + tupleOptional.get().first); log.info("We try to authenticate to a random peer. " + tupleOptional.get().first);
@ -245,6 +255,7 @@ public class PeerGroup {
// We try to connect to a reported peer. If we fail we repeat after the failed peer has been removed. // We try to connect to a reported peer. If we fail we repeat after the failed peer has been removed.
// If we succeed we repeat until we are ut of addresses. // If we succeed we repeat until we are ut of addresses.
private void authenticateToReportedPeer(Set<Address> remainingAddresses, Address peerAddress) { private void authenticateToReportedPeer(Set<Address> remainingAddresses, Address peerAddress) {
Log.traceCall();
checkArgument(!authenticatedPeers.containsKey(peerAddress), checkArgument(!authenticatedPeers.containsKey(peerAddress),
"We have that peer already authenticated. That must never happen."); "We have that peer already authenticated. That must never happen.");
@ -283,6 +294,7 @@ public class PeerGroup {
} }
private void authenticateToRemainingSeedNodes() { private void authenticateToRemainingSeedNodes() {
Log.traceCall();
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(seedNodeAddresses); Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(seedNodeAddresses);
if (tupleOptional.isPresent()) { if (tupleOptional.isPresent()) {
log.info("We try to authenticate to a random seed node. " + tupleOptional.get().first); log.info("We try to authenticate to a random seed node. " + tupleOptional.get().first);
@ -300,6 +312,7 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void authenticateToPeer(Address peerAddress, @Nullable Runnable authenticationCompleteHandler, @Nullable Runnable faultHandler) { public void authenticateToPeer(Address peerAddress, @Nullable Runnable authenticationCompleteHandler, @Nullable Runnable faultHandler) {
Log.traceCall();
checkArgument(!authenticatedPeers.containsKey(peerAddress), checkArgument(!authenticatedPeers.containsKey(peerAddress),
"We have that seed node already authenticated. That must never happen."); "We have that seed node already authenticated. That must never happen.");
@ -327,9 +340,10 @@ public class PeerGroup {
} }
private void setAuthenticated(Connection connection, Address peerAddress) { private void setAuthenticated(Connection connection, Address peerAddress) {
Log.traceCall();
log.info("\n\n############################################################\n" + log.info("\n\n############################################################\n" +
"We are authenticated to:" + "We are authenticated to:" +
"\nconnection=" + connection "\nconnection=" + connection.getUid()
+ "\nmyAddress=" + getMyAddress() + "\nmyAddress=" + getMyAddress()
+ "\npeerAddress= " + peerAddress + "\npeerAddress= " + peerAddress
+ "\n############################################################\n"); + "\n############################################################\n");
@ -342,18 +356,17 @@ public class PeerGroup {
} }
private void addAuthenticatedPeer(Peer peer) { private void addAuthenticatedPeer(Peer peer) {
Log.traceCall();
authenticatedPeers.put(peer.address, peer); authenticatedPeers.put(peer.address, peer);
reportedPeerAddresses.remove(peer.address); reportedPeerAddresses.remove(peer.address);
firstPeerAdded = !firstPeerAdded && authenticatedPeers.size() == 1; firstPeerAdded = !firstPeerAdded && authenticatedPeers.size() == 1;
UserThread.execute(() -> peerListeners.stream().forEach(e -> e.onPeerAdded(peer))); peerListeners.stream().forEach(e -> e.onPeerAdded(peer));
if (firstPeerAdded) if (firstPeerAdded)
UserThread.execute(() -> peerListeners.stream().forEach(e -> e.onFirstAuthenticatePeer(peer))); peerListeners.stream().forEach(e -> e.onFirstAuthenticatePeer(peer));
if (authenticatedPeers.size() > MAX_CONNECTIONS)
disconnectOldConnections();
if (!checkIfConnectedPeersExceeds())
printAuthenticatedPeers(); printAuthenticatedPeers();
} }
@ -362,13 +375,14 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void setupMaintenanceTimer() { private void setupMaintenanceTimer() {
maintenanceTimer.scheduleAtFixedRate(new TimerTask() { Log.traceCall();
sendPingTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setName("MaintenanceTimer-" + new Random().nextInt(1000)); Utilities.setThreadName("MaintenanceTimer");
try { try {
UserThread.execute(() -> { UserThread.execute(() -> {
disconnectOldConnections(); checkIfConnectedPeersExceeds();
pingPeers(); pingPeers();
}); });
} catch (Throwable t) { } catch (Throwable t) {
@ -376,14 +390,14 @@ public class PeerGroup {
log.error("Executing task failed. " + t.getMessage()); log.error("Executing task failed. " + t.getMessage());
} }
} }
}, MAINTENANCE_INTERVAL, MAINTENANCE_INTERVAL); }, SEND_PING_INTERVAL, SEND_PING_INTERVAL);
getPeersTimer.scheduleAtFixedRate(new TimerTask() { getPeersTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setName("GetPeersTimer-" + new Random().nextInt(1000)); Utilities.setThreadName("GetPeersTimer");
try { try {
UserThread.execute(() -> sendGetPeersRequest()); UserThread.execute(() -> trySendGetPeersRequest());
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
log.error("Executing task failed. " + t.getMessage()); log.error("Executing task failed. " + t.getMessage());
@ -393,21 +407,28 @@ public class PeerGroup {
} }
private void disconnectOldConnections() { private boolean checkIfConnectedPeersExceeds() {
Log.traceCall();
if (authenticatedPeers.size() > MAX_CONNECTIONS) {
log.trace("We have too many connections open. Lets remove the one which was not active recently.");
List<Connection> authenticatedConnections = networkNode.getAllConnections().stream() List<Connection> authenticatedConnections = networkNode.getAllConnections().stream()
.filter(e -> e.isAuthenticated()) .filter(e -> e.isAuthenticated())
.collect(Collectors.toList()); .collect(Collectors.toList());
if (authenticatedConnections.size() > MAX_CONNECTIONS) {
authenticatedConnections.sort((o1, o2) -> o1.getLastActivityDate().compareTo(o2.getLastActivityDate())); authenticatedConnections.sort((o1, o2) -> o1.getLastActivityDate().compareTo(o2.getLastActivityDate()));
log.info("Number of connections exceeds MAX_CONNECTIONS. Current size=" + authenticatedConnections.size()); log.info("Number of connections exceeds MAX_CONNECTIONS. Current size=" + authenticatedConnections.size());
Connection connection = authenticatedConnections.remove(0); Connection connection = authenticatedConnections.remove(0);
log.info("Shutdown oldest connection with last activity date=" + connection.getLastActivityDate() + " / connection=" + connection); log.info("We had shut down the oldest connection with last activity date="
connection.shutDown(() -> UserThread.runAfterRandomDelay(() -> disconnectOldConnections(), 100, 500, TimeUnit.MILLISECONDS)); + connection.getLastActivityDate() + " / connection=" + connection);
connection.shutDown(() -> UserThread.runAfterRandomDelay(() -> checkIfConnectedPeersExceeds(), 100, 500, TimeUnit.MILLISECONDS));
return true;
} else {
log.trace("We don't have too many connections open.");
return false;
} }
} }
private void pingPeers() { private void pingPeers() {
log.trace("pingPeers"); Log.traceCall();
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeers.values()); Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeers.values());
connectedPeersList.stream() connectedPeersList.stream()
.filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > PING_AFTER_CONNECTION_INACTIVITY) .filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > PING_AFTER_CONNECTION_INACTIVITY)
@ -428,9 +449,11 @@ public class PeerGroup {
}, 5, 10)); }, 5, 10));
} }
private void sendGetPeersRequest() { private void trySendGetPeersRequest() {
log.trace("sendGetPeersRequest"); Log.traceCall();
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeers.values()); Collection<Peer> peers = authenticatedPeers.values();
if (!peers.isEmpty()) {
Set<Peer> connectedPeersList = new HashSet<>(peers);
connectedPeersList.stream() connectedPeersList.stream()
.forEach(e -> UserThread.runAfterRandomDelay(() -> { .forEach(e -> UserThread.runAfterRandomDelay(() -> {
SettableFuture<Connection> future = networkNode.sendMessage(e.connection, SettableFuture<Connection> future = networkNode.sendMessage(e.connection,
@ -448,9 +471,13 @@ public class PeerGroup {
} }
}); });
}, 5, 10)); }, 5, 10));
} else {
log.debug("No peers available for requesting.");
}
} }
private void processMaintenanceMessage(MaintenanceMessage message, Connection connection) { private void processMaintenanceMessage(MaintenanceMessage message, Connection connection) {
Log.traceCall();
log.debug("Received message " + message + " at " + getMyAddress() + " from " + connection.getPeerAddress()); log.debug("Received message " + message + " at " + getMyAddress() + " from " + connection.getPeerAddress());
if (message instanceof PingMessage) { if (message instanceof PingMessage) {
SettableFuture<Connection> future = networkNode.sendMessage(connection, new PongMessage(((PingMessage) message).nonce)); SettableFuture<Connection> future = networkNode.sendMessage(connection, new PongMessage(((PingMessage) message).nonce));
@ -471,8 +498,8 @@ public class PeerGroup {
Peer peer = authenticatedPeers.get(connection.getPeerAddress()); Peer peer = authenticatedPeers.get(connection.getPeerAddress());
if (peer != null) { if (peer != null) {
if (((PongMessage) message).nonce != peer.getPingNonce()) { if (((PongMessage) message).nonce != peer.getPingNonce()) {
removePeer(peer.address);
log.warn("PongMessage invalid: self/peer " + getMyAddress() + "/" + connection.getPeerAddress()); log.warn("PongMessage invalid: self/peer " + getMyAddress() + "/" + connection.getPeerAddress());
removePeer(peer.address);
} }
} }
} }
@ -510,18 +537,22 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void addMessageListener(MessageListener messageListener) { public void addMessageListener(MessageListener messageListener) {
Log.traceCall();
networkNode.addMessageListener(messageListener); networkNode.addMessageListener(messageListener);
} }
public void removeMessageListener(MessageListener messageListener) { public void removeMessageListener(MessageListener messageListener) {
Log.traceCall();
networkNode.removeMessageListener(messageListener); networkNode.removeMessageListener(messageListener);
} }
public void addPeerListener(PeerListener peerListener) { public void addPeerListener(PeerListener peerListener) {
Log.traceCall();
peerListeners.add(peerListener); peerListeners.add(peerListener);
} }
public void removePeerListener(PeerListener peerListener) { public void removePeerListener(PeerListener peerListener) {
Log.traceCall();
peerListeners.remove(peerListener); peerListeners.remove(peerListener);
} }
@ -531,17 +562,20 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private Map<Address, Peer> getAuthenticatedPeers() { private Map<Address, Peer> getAuthenticatedPeers() {
Log.traceCall();
return authenticatedPeers; return authenticatedPeers;
} }
public Set<Address> getAllPeerAddresses() { public Set<Address> getAllPeerAddresses() {
CopyOnWriteArraySet<Address> allPeerAddresses = new CopyOnWriteArraySet<>(reportedPeerAddresses); Log.traceCall();
Set<Address> allPeerAddresses = new HashSet<>(reportedPeerAddresses);
allPeerAddresses.addAll(authenticatedPeers.values().stream() allPeerAddresses.addAll(authenticatedPeers.values().stream()
.map(e -> e.address).collect(Collectors.toList())); .map(e -> e.address).collect(Collectors.toSet()));
return allPeerAddresses; return allPeerAddresses;
} }
public Set<Address> getSeedNodeAddresses() { public Set<Address> getSeedNodeAddresses() {
Log.traceCall();
return seedNodeAddresses; return seedNodeAddresses;
} }
@ -551,7 +585,7 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
void addToReportedPeers(HashSet<Address> peerAddresses, Connection connection) { void addToReportedPeers(HashSet<Address> peerAddresses, Connection connection) {
log.trace("addToReportedPeers"); Log.traceCall();
// we disconnect misbehaving nodes trying to send too many peers // we disconnect misbehaving nodes trying to send too many peers
// reported peers include the peers connected peers which is normally max. 8 but we give some headroom // reported peers include the peers connected peers which is normally max. 8 but we give some headroom
// for safety // for safety
@ -560,24 +594,29 @@ public class PeerGroup {
} else { } else {
peerAddresses.remove(getMyAddress()); peerAddresses.remove(getMyAddress());
reportedPeerAddresses.addAll(peerAddresses); reportedPeerAddresses.addAll(peerAddresses);
purgeReportedPeers(); purgeReportedPeersIfExceeds();
} }
} }
private void purgeReportedPeers() { private void purgeReportedPeersIfExceeds() {
log.trace("purgeReportedPeers"); Log.traceCall();
int all = getAllPeerAddresses().size(); int size = reportedPeerAddresses.size();
if (all > 1000) { if (size > MAX_REPORTED_PEERS) {
int diff = all - 100; log.trace("We have more then {} reported peers. size={}. " +
"We remove random peers from the reported peers list.", MAX_REPORTED_PEERS, size);
int diff = size - MAX_REPORTED_PEERS;
List<Address> list = new LinkedList<>(getReportedNotConnectedPeerAddresses()); List<Address> list = new LinkedList<>(getReportedNotConnectedPeerAddresses());
for (int i = 0; i < diff; i++) { for (int i = 0; i < diff; i++) {
Address toRemove = getAndRemoveRandomItem(list); Address toRemove = getAndRemoveRandomItem(list);
reportedPeerAddresses.remove(toRemove); reportedPeerAddresses.remove(toRemove);
} }
} else {
log.trace("We don't have more then {} reported peers yet.", MAX_REPORTED_PEERS);
} }
} }
private Set<Address> getReportedNotConnectedPeerAddresses() { private Set<Address> getReportedNotConnectedPeerAddresses() {
Log.traceCall();
Set<Address> set = new HashSet<>(reportedPeerAddresses); Set<Address> set = new HashSet<>(reportedPeerAddresses);
authenticatedPeers.values().stream().forEach(e -> set.remove(e.address)); authenticatedPeers.values().stream().forEach(e -> set.remove(e.address));
return set; return set;
@ -589,18 +628,20 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void removePeer(@Nullable Address peerAddress) { private void removePeer(@Nullable Address peerAddress) {
reportedPeerAddresses.remove(peerAddress); Log.traceCall("peerAddress=" + peerAddress);
if (peerAddress != null) { if (peerAddress != null) {
boolean contained = reportedPeerAddresses.remove(peerAddress);
Peer disconnectedPeer = authenticatedPeers.remove(peerAddress); Peer disconnectedPeer = authenticatedPeers.remove(peerAddress);
if (disconnectedPeer != null) if (disconnectedPeer != null)
UserThread.execute(() -> peerListeners.stream().forEach(e -> e.onPeerRemoved(peerAddress))); peerListeners.stream().forEach(e -> e.onPeerRemoved(peerAddress));
if (contained || disconnectedPeer != null)
printAllPeers();
} }
printAuthenticatedPeers();
printReportedPeers();
} }
private Address getMyAddress() { private Address getMyAddress() {
Log.traceCall();
return networkNode.getAddress(); return networkNode.getAddress();
} }
@ -610,10 +651,12 @@ public class PeerGroup {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private Address getAndRemoveRandomItem(List<Address> list) { private Address getAndRemoveRandomItem(List<Address> list) {
Log.traceCall();
return list.remove(new Random().nextInt(list.size())); return list.remove(new Random().nextInt(list.size()));
} }
private Optional<Tuple2<Address, Set<Address>>> getRandomItemAndRemainingSet(Set<Address> remainingAddresses) { private Optional<Tuple2<Address, Set<Address>>> getRandomItemAndRemainingSet(Set<Address> remainingAddresses) {
Log.traceCall();
List<Address> list = new ArrayList<>(remainingAddresses); List<Address> list = new ArrayList<>(remainingAddresses);
authenticatedPeers.values().stream().forEach(e -> list.remove(e.address)); authenticatedPeers.values().stream().forEach(e -> list.remove(e.address));
if (!list.isEmpty()) { if (!list.isEmpty()) {
@ -625,11 +668,13 @@ public class PeerGroup {
} }
public void printAllPeers() { public void printAllPeers() {
Log.traceCall();
printAuthenticatedPeers(); printAuthenticatedPeers();
printReportedPeers(); printReportedPeers();
} }
public void printAuthenticatedPeers() { public void printAuthenticatedPeers() {
Log.traceCall();
StringBuilder result = new StringBuilder("\n\n############################################################\n" + StringBuilder result = new StringBuilder("\n\n############################################################\n" +
"Authenticated peers for node " + getMyAddress() + ":"); "Authenticated peers for node " + getMyAddress() + ":");
authenticatedPeers.values().stream().forEach(e -> result.append("\n").append(e.address)); authenticatedPeers.values().stream().forEach(e -> result.append("\n").append(e.address));
@ -638,6 +683,7 @@ public class PeerGroup {
} }
public void printReportedPeers() { public void printReportedPeers() {
Log.traceCall();
StringBuilder result = new StringBuilder("\n\n############################################################\n" + StringBuilder result = new StringBuilder("\n\n############################################################\n" +
"Reported peers for node " + getMyAddress() + ":"); "Reported peers for node " + getMyAddress() + ":");
reportedPeerAddresses.stream().forEach(e -> result.append("\n").append(e)); reportedPeerAddresses.stream().forEach(e -> result.append("\n").append(e));

View file

@ -1,5 +1,6 @@
package io.bitsquare.p2p.seed; package io.bitsquare.p2p.seed;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.EncryptionService;
@ -29,6 +30,7 @@ public class SeedNode {
private boolean stopped; private boolean stopped;
public SeedNode() { public SeedNode() {
Log.traceCall();
} }
@ -41,6 +43,7 @@ public class SeedNode {
// eg. lmvdenjkyvx2ovga.onion:8001 20 false eo5ay2lyzrfvx2nr.onion:8002|si3uu56adkyqkldl.onion:8003 // eg. lmvdenjkyvx2ovga.onion:8001 20 false eo5ay2lyzrfvx2nr.onion:8002|si3uu56adkyqkldl.onion:8003
// or when using localhost: localhost:8001 20 true localhost:8002|localhost:8003 // or when using localhost: localhost:8001 20 true localhost:8002|localhost:8003
public void processArgs(String[] args) { public void processArgs(String[] args) {
Log.traceCall();
if (args.length > 0) { if (args.length > 0) {
String arg0 = args[0]; String arg0 = args[0];
checkArgument(arg0.contains(":") && arg0.split(":").length == 2 && arg0.split(":")[1].length() == 4, "Wrong program argument"); checkArgument(arg0.contains(":") && arg0.split(":").length == 2 && arg0.split(":")[1].length() == 4, "Wrong program argument");
@ -78,6 +81,7 @@ public class SeedNode {
} }
public void createAndStartP2PService(EncryptionService encryptionService, KeyRing keyRing, Address mySeedNodeAddress, boolean useLocalhost, @Nullable Set<Address> seedNodes, @Nullable P2PServiceListener listener) { public void createAndStartP2PService(EncryptionService encryptionService, KeyRing keyRing, Address mySeedNodeAddress, boolean useLocalhost, @Nullable Set<Address> seedNodes, @Nullable P2PServiceListener listener) {
Log.traceCall();
SeedNodesRepository seedNodesRepository = new SeedNodesRepository(); SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
if (seedNodes != null && !seedNodes.isEmpty()) { if (seedNodes != null && !seedNodes.isEmpty()) {
if (useLocalhost) if (useLocalhost)
@ -92,14 +96,17 @@ public class SeedNode {
} }
public P2PService getP2PService() { public P2PService getP2PService() {
Log.traceCall();
return p2PService; return p2PService;
} }
public void shutDown() { public void shutDown() {
Log.traceCall();
shutDown(null); shutDown(null);
} }
public void shutDown(@Nullable Runnable shutDownCompleteHandler) { public void shutDown(@Nullable Runnable shutDownCompleteHandler) {
Log.traceCall();
log.debug("Request shutdown seed node"); log.debug("Request shutdown seed node");
if (!stopped) { if (!stopped) {
stopped = true; stopped = true;

View file

@ -1,10 +1,12 @@
package io.bitsquare.p2p.storage; package io.bitsquare.p2p.storage;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.CryptoException; import io.bitsquare.common.crypto.CryptoException;
import io.bitsquare.common.crypto.Hash; import io.bitsquare.common.crypto.Hash;
import io.bitsquare.common.crypto.Sig; import io.bitsquare.common.crypto.Sig;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
import io.bitsquare.p2p.network.IllegalRequest; import io.bitsquare.p2p.network.IllegalRequest;
import io.bitsquare.p2p.network.MessageListener; import io.bitsquare.p2p.network.MessageListener;
@ -21,12 +23,12 @@ import java.math.BigInteger;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
// Run in UserThread
public class ProtectedExpirableDataStorage { public class ProtectedExpirableDataStorage {
private static final Logger log = LoggerFactory.getLogger(ProtectedExpirableDataStorage.class); private static final Logger log = LoggerFactory.getLogger(ProtectedExpirableDataStorage.class);
@ -48,6 +50,7 @@ public class ProtectedExpirableDataStorage {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ProtectedExpirableDataStorage(PeerGroup peerGroup, File storageDir) { public ProtectedExpirableDataStorage(PeerGroup peerGroup, File storageDir) {
Log.traceCall();
this.peerGroup = peerGroup; this.peerGroup = peerGroup;
storage = new Storage<>(storageDir); storage = new Storage<>(storageDir);
@ -56,12 +59,14 @@ public class ProtectedExpirableDataStorage {
} }
private void init() { private void init() {
Log.traceCall();
ConcurrentHashMap<BigInteger, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "sequenceNumberMap"); ConcurrentHashMap<BigInteger, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "sequenceNumberMap");
if (persisted != null) { if (persisted != null) {
sequenceNumberMap = persisted; sequenceNumberMap = persisted;
} }
addMessageListener((message, connection) -> { addMessageListener((message, connection) -> {
Log.traceCall("onMessage: Message=" + message);
if (message instanceof DataMessage) { if (message instanceof DataMessage) {
if (connection.isAuthenticated()) { if (connection.isAuthenticated()) {
log.trace("ProtectedExpirableDataMessage received " + message + " on connection " + connection); log.trace("ProtectedExpirableDataMessage received " + message + " on connection " + connection);
@ -80,21 +85,26 @@ public class ProtectedExpirableDataStorage {
} }
}); });
TimerTask task = new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setName("RemoveExpiredEntriesTimer-" + new Random().nextInt(1000));
try { try {
log.info("removeExpiredEntries called "); Utilities.setThreadName("RemoveExpiredEntriesTimer");
map.entrySet().stream().filter(entry -> entry.getValue().isExpired()) UserThread.execute(() -> removeExpiredEntries());
.forEach(entry -> map.remove(entry.getKey()));
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
log.error("Executing task failed. " + t.getMessage()); log.error("Executing task failed. " + t.getMessage());
} }
} }
}; },
timer.scheduleAtFixedRate(task, CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL); CHECK_TTL_INTERVAL, CHECK_TTL_INTERVAL);
}
private void removeExpiredEntries() {
Log.traceCall();
map.entrySet().stream()
.filter(entry -> entry.getValue().isExpired())
.forEach(entry -> map.remove(entry.getKey()));
} }
@ -103,6 +113,7 @@ public class ProtectedExpirableDataStorage {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void shutDown() { public void shutDown() {
Log.traceCall();
if (!shutDownInProgress) { if (!shutDownInProgress) {
shutDownInProgress = true; shutDownInProgress = true;
timer.cancel(); timer.cancel();
@ -111,10 +122,12 @@ public class ProtectedExpirableDataStorage {
} }
public void setAuthenticated() { public void setAuthenticated() {
Log.traceCall();
this.authenticated = true; this.authenticated = true;
} }
public boolean add(ProtectedData protectedData, @Nullable Address sender) { public boolean add(ProtectedData protectedData, @Nullable Address sender) {
Log.traceCall();
BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload); BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload);
boolean containsKey = map.containsKey(hashOfPayload); boolean containsKey = map.containsKey(hashOfPayload);
boolean result = checkPublicKeys(protectedData, true) boolean result = checkPublicKeys(protectedData, true)
@ -128,13 +141,13 @@ public class ProtectedExpirableDataStorage {
if (result) { if (result) {
map.put(hashOfPayload, protectedData); map.put(hashOfPayload, protectedData);
log.trace("Data added to our map and it will be broadcasted to our peers."); log.trace("Data added to our map and it will be broadcasted to our peers.");
UserThread.execute(() -> hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedData))); hashMapChangedListeners.stream().forEach(e -> e.onAdded(protectedData));
StringBuilder sb = new StringBuilder("\n\n############################################################\n" + StringBuilder sb = new StringBuilder("\n\n############################################################\n");
"Data set after addProtectedExpirableData:"); sb.append("Data set after addProtectedExpirableData:");
map.values().stream().forEach(e -> sb.append("\n").append(e.toString())); map.values().stream().forEach(e -> sb.append("\n").append(e.toString()).append("\n"));
sb.append("\n############################################################\n"); sb.append("\n############################################################\n");
log.trace(sb.toString()); log.info(sb.toString());
if (!containsKey) if (!containsKey)
broadcast(new AddDataMessage(protectedData), sender); broadcast(new AddDataMessage(protectedData), sender);
@ -148,6 +161,7 @@ public class ProtectedExpirableDataStorage {
} }
public boolean remove(ProtectedData protectedData, @Nullable Address sender) { public boolean remove(ProtectedData protectedData, @Nullable Address sender) {
Log.traceCall();
BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload); BigInteger hashOfPayload = getHashAsBigInteger(protectedData.expirablePayload);
boolean containsKey = map.containsKey(hashOfPayload); boolean containsKey = map.containsKey(hashOfPayload);
if (!containsKey) log.debug("Remove data ignored as we don't have an entry for that data."); if (!containsKey) log.debug("Remove data ignored as we don't have an entry for that data.");
@ -172,6 +186,7 @@ public class ProtectedExpirableDataStorage {
} }
public boolean removeMailboxData(ProtectedMailboxData protectedMailboxData, @Nullable Address sender) { public boolean removeMailboxData(ProtectedMailboxData protectedMailboxData, @Nullable Address sender) {
Log.traceCall();
BigInteger hashOfData = getHashAsBigInteger(protectedMailboxData.expirablePayload); BigInteger hashOfData = getHashAsBigInteger(protectedMailboxData.expirablePayload);
boolean containsKey = map.containsKey(hashOfData); boolean containsKey = map.containsKey(hashOfData);
if (!containsKey) log.debug("Remove data ignored as we don't have an entry for that data."); if (!containsKey) log.debug("Remove data ignored as we don't have an entry for that data.");
@ -196,11 +211,13 @@ public class ProtectedExpirableDataStorage {
} }
public Map<BigInteger, ProtectedData> getMap() { public Map<BigInteger, ProtectedData> getMap() {
Log.traceCall();
return map; return map;
} }
public ProtectedData getDataWithSignedSeqNr(ExpirablePayload payload, KeyPair ownerStoragePubKey) public ProtectedData getDataWithSignedSeqNr(ExpirablePayload payload, KeyPair ownerStoragePubKey)
throws CryptoException { throws CryptoException {
Log.traceCall();
BigInteger hashOfData = getHashAsBigInteger(payload); BigInteger hashOfData = getHashAsBigInteger(payload);
int sequenceNumber; int sequenceNumber;
if (sequenceNumberMap.containsKey(hashOfData)) if (sequenceNumberMap.containsKey(hashOfData))
@ -216,6 +233,7 @@ public class ProtectedExpirableDataStorage {
public ProtectedMailboxData getMailboxDataWithSignedSeqNr(ExpirableMailboxPayload expirableMailboxPayload, public ProtectedMailboxData getMailboxDataWithSignedSeqNr(ExpirableMailboxPayload expirableMailboxPayload,
KeyPair storageSignaturePubKey, PublicKey receiversPublicKey) KeyPair storageSignaturePubKey, PublicKey receiversPublicKey)
throws CryptoException { throws CryptoException {
Log.traceCall();
BigInteger hashOfData = getHashAsBigInteger(expirableMailboxPayload); BigInteger hashOfData = getHashAsBigInteger(expirableMailboxPayload);
int sequenceNumber; int sequenceNumber;
if (sequenceNumberMap.containsKey(hashOfData)) if (sequenceNumberMap.containsKey(hashOfData))
@ -230,10 +248,12 @@ public class ProtectedExpirableDataStorage {
} }
public void addHashMapChangedListener(HashMapChangedListener hashMapChangedListener) { public void addHashMapChangedListener(HashMapChangedListener hashMapChangedListener) {
Log.traceCall();
hashMapChangedListeners.add(hashMapChangedListener); hashMapChangedListeners.add(hashMapChangedListener);
} }
private void addMessageListener(MessageListener messageListener) { private void addMessageListener(MessageListener messageListener) {
Log.traceCall();
peerGroup.addMessageListener(messageListener); peerGroup.addMessageListener(messageListener);
} }
@ -243,9 +263,10 @@ public class ProtectedExpirableDataStorage {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void doRemoveProtectedExpirableData(ProtectedData protectedData, BigInteger hashOfPayload) { private void doRemoveProtectedExpirableData(ProtectedData protectedData, BigInteger hashOfPayload) {
Log.traceCall();
map.remove(hashOfPayload); map.remove(hashOfPayload);
log.trace("Data removed from our map. We broadcast the message to our peers."); log.trace("Data removed from our map. We broadcast the message to our peers.");
UserThread.execute(() -> hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedData))); hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedData));
StringBuilder sb = new StringBuilder("\n\n############################################################\n" + StringBuilder sb = new StringBuilder("\n\n############################################################\n" +
"Data set after removeProtectedExpirableData:"); "Data set after removeProtectedExpirableData:");
@ -255,6 +276,7 @@ public class ProtectedExpirableDataStorage {
} }
private boolean isSequenceNrValid(ProtectedData data, BigInteger hashOfData) { private boolean isSequenceNrValid(ProtectedData data, BigInteger hashOfData) {
Log.traceCall();
int newSequenceNumber = data.sequenceNumber; int newSequenceNumber = data.sequenceNumber;
Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData); Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData);
if (sequenceNumberMap.containsKey(hashOfData) && newSequenceNumber <= storedSequenceNumber) { if (sequenceNumberMap.containsKey(hashOfData) && newSequenceNumber <= storedSequenceNumber) {
@ -267,6 +289,7 @@ public class ProtectedExpirableDataStorage {
} }
private boolean checkSignature(ProtectedData data) { private boolean checkSignature(ProtectedData data) {
Log.traceCall();
byte[] hashOfDataAndSeqNr = Hash.getHash(new DataAndSeqNr(data.expirablePayload, data.sequenceNumber)); byte[] hashOfDataAndSeqNr = Hash.getHash(new DataAndSeqNr(data.expirablePayload, data.sequenceNumber));
try { try {
boolean result = Sig.verify(data.ownerStoragePubKey, hashOfDataAndSeqNr, data.signature); boolean result = Sig.verify(data.ownerStoragePubKey, hashOfDataAndSeqNr, data.signature);
@ -282,6 +305,7 @@ public class ProtectedExpirableDataStorage {
} }
private boolean checkPublicKeys(ProtectedData data, boolean isAddOperation) { private boolean checkPublicKeys(ProtectedData data, boolean isAddOperation) {
Log.traceCall();
boolean result = false; boolean result = false;
if (data.expirablePayload instanceof ExpirableMailboxPayload) { if (data.expirablePayload instanceof ExpirableMailboxPayload) {
ExpirableMailboxPayload expirableMailboxPayload = (ExpirableMailboxPayload) data.expirablePayload; ExpirableMailboxPayload expirableMailboxPayload = (ExpirableMailboxPayload) data.expirablePayload;
@ -299,6 +323,7 @@ public class ProtectedExpirableDataStorage {
} }
private boolean checkIfStoredDataMatchesNewData(ProtectedData data, BigInteger hashOfData) { private boolean checkIfStoredDataMatchesNewData(ProtectedData data, BigInteger hashOfData) {
Log.traceCall();
ProtectedData storedData = map.get(hashOfData); ProtectedData storedData = map.get(hashOfData);
boolean result = getHashAsBigInteger(storedData.expirablePayload).equals(hashOfData) boolean result = getHashAsBigInteger(storedData.expirablePayload).equals(hashOfData)
&& storedData.ownerStoragePubKey.equals(data.ownerStoragePubKey); && storedData.ownerStoragePubKey.equals(data.ownerStoragePubKey);
@ -309,6 +334,7 @@ public class ProtectedExpirableDataStorage {
} }
private boolean checkIfStoredMailboxDataMatchesNewMailboxData(ProtectedMailboxData data, BigInteger hashOfData) { private boolean checkIfStoredMailboxDataMatchesNewMailboxData(ProtectedMailboxData data, BigInteger hashOfData) {
Log.traceCall();
ProtectedData storedData = map.get(hashOfData); ProtectedData storedData = map.get(hashOfData);
if (storedData instanceof ProtectedMailboxData) { if (storedData instanceof ProtectedMailboxData) {
ProtectedMailboxData storedMailboxData = (ProtectedMailboxData) storedData; ProtectedMailboxData storedMailboxData = (ProtectedMailboxData) storedData;
@ -325,8 +351,8 @@ public class ProtectedExpirableDataStorage {
} }
} }
private void broadcast(BroadcastMessage message, @Nullable Address sender) { private void broadcast(BroadcastMessage message, @Nullable Address sender) {
Log.traceCall();
if (authenticated) { if (authenticated) {
peerGroup.broadcast(message, sender); peerGroup.broadcast(message, sender);
log.trace("Broadcast message " + message); log.trace("Broadcast message " + message);
@ -336,6 +362,7 @@ public class ProtectedExpirableDataStorage {
} }
private BigInteger getHashAsBigInteger(ExpirablePayload payload) { private BigInteger getHashAsBigInteger(ExpirablePayload payload) {
Log.traceCall();
return new BigInteger(Hash.getHash(payload)); return new BigInteger(Hash.getHash(payload));
} }
} }

View file

@ -82,7 +82,7 @@ public class TestUtils {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
seedNode.createAndStartP2PService(encryptionService, keyRing, new Address("localhost", port), useLocalhost, seedNodes, new P2PServiceListener() { seedNode.createAndStartP2PService(encryptionService, keyRing, new Address("localhost", port), useLocalhost, seedNodes, new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override
@ -120,7 +120,7 @@ public class TestUtils {
P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost, encryptionService, keyRing, new File("dummy")); P2PService p2PService = new P2PService(seedNodesRepository, port, new File("seed_node_" + port), useLocalhost, encryptionService, keyRing, new File("dummy"));
p2PService.start(new P2PServiceListener() { p2PService.start(new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
} }
@Override @Override

View file

@ -85,7 +85,7 @@ public class PeerGroupTest {
latch = new CountDownLatch(2); latch = new CountDownLatch(2);
seedNode1.createAndStartP2PService(null, null, address, useLocalhost, seedNodes, new P2PServiceListener() { seedNode1.createAndStartP2PService(null, null, address, useLocalhost, seedNodes, new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
latch.countDown(); latch.countDown();
} }
@ -129,7 +129,7 @@ public class PeerGroupTest {
seedNode1 = new SeedNode(); seedNode1 = new SeedNode();
seedNode1.createAndStartP2PService(null, null, address1, useLocalhost, seedNodes, new P2PServiceListener() { seedNode1.createAndStartP2PService(null, null, address1, useLocalhost, seedNodes, new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
latch.countDown(); latch.countDown();
} }
@ -160,7 +160,7 @@ public class PeerGroupTest {
seedNode2 = new SeedNode(); seedNode2 = new SeedNode();
seedNode2.createAndStartP2PService(null, null, address2, useLocalhost, seedNodes, new P2PServiceListener() { seedNode2.createAndStartP2PService(null, null, address2, useLocalhost, seedNodes, new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
latch.countDown(); latch.countDown();
} }
@ -387,7 +387,7 @@ public class PeerGroupTest {
latch = new CountDownLatch(1); latch = new CountDownLatch(1);
seedNode.createAndStartP2PService(null, null, new Address("localhost", port), useLocalhost, seedNodes, new P2PServiceListener() { seedNode.createAndStartP2PService(null, null, new Address("localhost", port), useLocalhost, seedNodes, new P2PServiceListener() {
@Override @Override
public void onAllDataReceived() { public void onRequestingDataCompleted() {
latch.countDown(); latch.countDown();
} }

View file

@ -1,7 +1,7 @@
package io.bitsquare.p2p.seed; package io.bitsquare.p2p.seed;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.bitsquare.app.Logging; import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import org.bitcoinj.crypto.DRMWorkaround; import org.bitcoinj.crypto.DRMWorkaround;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
@ -28,7 +28,8 @@ public class SeedNodeMain {
// To stop enter: q // To stop enter: q
public static void main(String[] args) throws NoSuchAlgorithmException { public static void main(String[] args) throws NoSuchAlgorithmException {
Path path = Paths.get("seed_node_log"); Path path = Paths.get("seed_node_log");
Logging.setup(path.toString()); Log.setup(path.toString());
Log.PRINT_TRACE_METHOD = true;
log.info("Log files under: " + path.toAbsolutePath().toString()); log.info("Log files under: " + path.toAbsolutePath().toString());
DRMWorkaround.maybeDisableExportControls(); DRMWorkaround.maybeDisableExportControls();
@ -60,7 +61,7 @@ public class SeedNodeMain {
public void listenForExitCommand() { public void listenForExitCommand() {
Scanner scan = new Scanner(System.in); Scanner scan = new Scanner(System.in);
String line; String line;
while (!stopped && ((line = scan.nextLine()) != null)) { while (!stopped && !Thread.currentThread().isInterrupted() && ((line = scan.nextLine()) != null)) {
if (line.equals("q")) { if (line.equals("q")) {
if (!stopped) { if (!stopped) {
stopped = true; stopped = true;
@ -70,11 +71,11 @@ public class SeedNodeMain {
}, 5); }, 5);
if (seedNode != null) { if (seedNode != null) {
seedNode.shutDown(() -> { UserThread.execute(() -> seedNode.shutDown(() -> {
timeout.cancel(); timeout.cancel();
log.debug("Shutdown seed node complete."); log.debug("Shutdown seed node complete.");
System.exit(0); System.exit(0);
}); }));
} }
} }
} }

View file

@ -19,11 +19,11 @@
<configuration> <configuration>
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %xEx%n</pattern> <pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15} - %msg %xEx%n)</pattern>
</encoder> </encoder>
</appender> </appender>
<root level="INFO"> <root level="TRACE">
<appender-ref ref="CONSOLE_APPENDER"/> <appender-ref ref="CONSOLE_APPENDER"/>
</root> </root>
<!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="INFO"/> <!-- <logger name="io.bitsquare.p2p.peers.PeerGroup" level="INFO"/>
@ -34,6 +34,7 @@
<logger name="io.bitsquare.p2p.network.NetworkNode" level="TRACE"/>--> <logger name="io.bitsquare.p2p.network.NetworkNode" level="TRACE"/>-->
<logger name="io.bitsquare.storage.Storage" level="WARN"/>
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/> <logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/>
</configuration> </configuration>