package io.bitsquare.p2p.network;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.NodeAddress;
import io.nucleo.net.HiddenServiceDescriptor;
import io.nucleo.net.TorNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
// Run in UserThread
public class LocalhostNetworkNode extends NetworkNode {
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNode.class);
private static volatile int simulateTorDelayTorNode = 500;
private static volatile int simulateTorDelayHiddenService = 500;
public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) {
LocalhostNetworkNode.simulateTorDelayTorNode = simulateTorDelayTorNode;
}
public static void setSimulateTorDelayHiddenService(int simulateTorDelayHiddenService) {
LocalhostNetworkNode.simulateTorDelayHiddenService = simulateTorDelayHiddenService;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public LocalhostNetworkNode(int port) {
super(port);
}
@Override
public void start(@Nullable SetupListener setupListener) {
if (setupListener != null)
addSetupListener(setupListener);
createExecutorService();
//Tor delay simulation
createTorNode(torNode -> {
Log.traceCall("torNode created");
setupListeners.stream().forEach(SetupListener::onTorNodeReady);
// Create Hidden Service (takes about 40 sec.)
createHiddenService(hiddenServiceDescriptor -> {
Log.traceCall("hiddenService created");
try {
startServer(new ServerSocket(servicePort));
} catch (IOException e) {
e.printStackTrace();
log.error("Exception at startServer: " + e.getMessage());
}
nodeAddressProperty.set(new NodeAddress("localhost", servicePort));
setupListeners.stream().forEach(SetupListener::onHiddenServicePublished);
});
});
}
// Called from NetworkNode thread
@Override
protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException {
return new Socket(peerNodeAddress.hostName, peerNodeAddress.port);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tor delay simulation
///////////////////////////////////////////////////////////////////////////////////////////
private void createTorNode(final Consumer<TorNode> resultHandler) {
ListenableFuture<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> future = executorService.submit(() -> {
Utilities.setThreadName("NetworkNode:CreateTorNode");
long ts = System.currentTimeMillis();
if (simulateTorDelayTorNode > 0)
Uninterruptibles.sleepUninterruptibly(simulateTorDelayTorNode, TimeUnit.MILLISECONDS);
log.debug("\n\n############################################################\n" +
"TorNode created [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n");
return null;
});
Futures.addCallback(future, new FutureCallback<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>>() {
public void onSuccess(TorNode<JavaOnionProxyManager, JavaOnionProxyContext> torNode) {
UserThread.execute(() -> {
// as we are simulating we return null
resultHandler.accept(null);
});
}
public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> {
log.error("[simulation] TorNode creation failed. " + throwable.getMessage());
throwable.printStackTrace();
});
}
});
}
private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) {
ListenableFuture<HiddenServiceDescriptor> future = executorService.submit(() -> {
Utilities.setThreadName("NetworkNode:CreateHiddenService");
long ts = System.currentTimeMillis();
if (simulateTorDelayHiddenService > 0)
Uninterruptibles.sleepUninterruptibly(simulateTorDelayHiddenService, TimeUnit.MILLISECONDS);
log.debug("\n\n############################################################\n" +
"Hidden service published [simulation]:" +
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
+ "\n############################################################\n");
return null;
});
Futures.addCallback(future, new FutureCallback<HiddenServiceDescriptor>() {
public void onSuccess(HiddenServiceDescriptor hiddenServiceDescriptor) {
UserThread.execute(() -> {
// as we are simulating we return null
resultHandler.accept(null);
});
}
public void onFailure(@NotNull Throwable throwable) {
UserThread.execute(() -> {
log.error("[simulation] Hidden service creation failed. " + throwable.getMessage());
throwable.printStackTrace();
});
}
});
}
}