mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-26 07:40:35 -04:00
add exception handling to runnables
This commit is contained in:
parent
4d82467fa3
commit
0b7e45ca55
15 changed files with 463 additions and 385 deletions
|
@ -39,7 +39,6 @@ import com.google.common.io.Files;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import org.bitcoinj.core.Utils;
|
import org.bitcoinj.core.Utils;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -49,7 +48,6 @@ import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
@ -62,8 +60,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
*/
|
*/
|
||||||
public class FileManager<T> {
|
public class FileManager<T> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(FileManager.class);
|
private static final Logger log = LoggerFactory.getLogger(FileManager.class);
|
||||||
private static final ReentrantLock lock = Threading.lock("FileManager");
|
|
||||||
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
|
||||||
|
|
||||||
private final File dir;
|
private final File dir;
|
||||||
private final File storageFile;
|
private final File storageFile;
|
||||||
|
@ -85,7 +81,7 @@ public class FileManager<T> {
|
||||||
|
|
||||||
ThreadFactoryBuilder builder = new ThreadFactoryBuilder()
|
ThreadFactoryBuilder builder = new ThreadFactoryBuilder()
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
.setNameFormat("FileManager thread")
|
.setNameFormat("FileManager-%d")
|
||||||
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
||||||
|
|
||||||
// An executor that starts up threads when needed and shuts them down later.
|
// An executor that starts up threads when needed and shuts them down later.
|
||||||
|
@ -144,40 +140,32 @@ public class FileManager<T> {
|
||||||
executor.schedule(saver, delay, delayTimeUnit);
|
executor.schedule(saver, delay, delayTimeUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T read(File file) {
|
public synchronized T read(File file) {
|
||||||
log.debug("read" + file);
|
log.debug("read" + file);
|
||||||
lock.lock();
|
|
||||||
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
||||||
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
||||||
return (T) objectInputStream.readObject();
|
return (T) objectInputStream.readObject();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.error("Exception at read: " + t.getMessage());
|
log.error("Exception at read: " + t.getMessage());
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeFile(String fileName) {
|
public synchronized void removeFile(String fileName) {
|
||||||
log.debug("removeFile" + fileName);
|
log.debug("removeFile" + fileName);
|
||||||
File file = new File(dir, fileName);
|
File file = new File(dir, fileName);
|
||||||
lock.lock();
|
boolean result = file.delete();
|
||||||
try {
|
if (!result)
|
||||||
boolean result = file.delete();
|
log.warn("Could not delete file: " + file.toString());
|
||||||
if (!result)
|
|
||||||
log.warn("Could not delete file: " + file.toString());
|
|
||||||
|
|
||||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
if (backupDir.exists()) {
|
if (backupDir.exists()) {
|
||||||
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
|
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
|
||||||
if (backupFile.exists()) {
|
if (backupFile.exists()) {
|
||||||
result = backupFile.delete();
|
result = backupFile.delete();
|
||||||
if (!result)
|
if (!result)
|
||||||
log.warn("Could not delete backupFile: " + file.toString());
|
log.warn("Could not delete backupFile: " + file.toString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,34 +188,24 @@ public class FileManager<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAndBackupFile(String fileName) throws IOException {
|
public synchronized void removeAndBackupFile(String fileName) throws IOException {
|
||||||
lock.lock();
|
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "corrupted").toString());
|
||||||
try {
|
if (!corruptedBackupDir.exists())
|
||||||
File corruptedBackupDir = new File(Paths.get(dir.getAbsolutePath(), "corrupted").toString());
|
if (!corruptedBackupDir.mkdir())
|
||||||
if (!corruptedBackupDir.exists())
|
log.warn("make dir failed");
|
||||||
if (!corruptedBackupDir.mkdir())
|
|
||||||
log.warn("make dir failed");
|
|
||||||
|
|
||||||
File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "corrupted", fileName).toString());
|
File corruptedFile = new File(Paths.get(dir.getAbsolutePath(), "corrupted", fileName).toString());
|
||||||
renameTempFileToFile(storageFile, corruptedFile);
|
renameTempFileToFile(storageFile, corruptedFile);
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void backupFile(String fileName) throws IOException {
|
public synchronized void backupFile(String fileName) throws IOException {
|
||||||
lock.lock();
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
try {
|
if (!backupDir.exists())
|
||||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
if (!backupDir.mkdir())
|
||||||
if (!backupDir.exists())
|
log.warn("make dir failed");
|
||||||
if (!backupDir.mkdir())
|
|
||||||
log.warn("make dir failed");
|
|
||||||
|
|
||||||
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
|
File backupFile = new File(Paths.get(dir.getAbsolutePath(), "backup", fileName).toString());
|
||||||
Files.copy(storageFile, backupFile);
|
Files.copy(storageFile, backupFile);
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -240,8 +218,7 @@ public class FileManager<T> {
|
||||||
UserThread.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now));
|
UserThread.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveToFile(T serializable, File dir, File storageFile) {
|
private synchronized void saveToFile(T serializable, File dir, File storageFile) {
|
||||||
lock.lock();
|
|
||||||
File tempFile = null;
|
File tempFile = null;
|
||||||
FileOutputStream fileOutputStream = null;
|
FileOutputStream fileOutputStream = null;
|
||||||
ObjectOutputStream objectOutputStream = null;
|
ObjectOutputStream objectOutputStream = null;
|
||||||
|
@ -292,27 +269,21 @@ public class FileManager<T> {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("Cannot close resources." + e.getMessage());
|
log.error("Cannot close resources." + e.getMessage());
|
||||||
}
|
}
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renameTempFileToFile(File tempFile, File file) throws IOException {
|
private synchronized void renameTempFileToFile(File tempFile, File file) throws IOException {
|
||||||
lock.lock();
|
if (Utils.isWindows()) {
|
||||||
try {
|
// Work around an issue on Windows whereby you can't rename over existing files.
|
||||||
if (Utils.isWindows()) {
|
final File canonical = file.getCanonicalFile();
|
||||||
// Work around an issue on Windows whereby you can't rename over existing files.
|
if (canonical.exists() && !canonical.delete()) {
|
||||||
final File canonical = file.getCanonicalFile();
|
throw new IOException("Failed to delete canonical file for replacement with save");
|
||||||
if (canonical.exists() && !canonical.delete()) {
|
|
||||||
throw new IOException("Failed to delete canonical file for replacement with save");
|
|
||||||
}
|
|
||||||
if (!tempFile.renameTo(canonical)) {
|
|
||||||
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
|
|
||||||
}
|
|
||||||
} else if (!tempFile.renameTo(file)) {
|
|
||||||
throw new IOException("Failed to rename " + tempFile + " to " + file);
|
|
||||||
}
|
}
|
||||||
} finally {
|
if (!tempFile.renameTo(canonical)) {
|
||||||
lock.unlock();
|
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
|
||||||
|
}
|
||||||
|
} else if (!tempFile.renameTo(file)) {
|
||||||
|
throw new IOException("Failed to rename " + tempFile + " to " + file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,9 +245,18 @@ public class WalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||||
walletAppKit.stopAsync();
|
Context ctx = Context.get();
|
||||||
walletAppKit.awaitTerminated();
|
new Thread(() -> {
|
||||||
initialize(seed, resultHandler, exceptionHandler);
|
try {
|
||||||
|
Context.propagate(ctx);
|
||||||
|
walletAppKit.stopAsync();
|
||||||
|
walletAppKit.awaitTerminated();
|
||||||
|
initialize(seed, resultHandler, exceptionHandler);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
|
}, "RestoreWallet-%d").start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.bitsquare.crypto;
|
package io.bitsquare.crypto;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||||
|
@ -8,6 +9,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
//TODO: Borrowed form BitcoinJ/Lighthouse. Remove Protos dependency, check complete code logic.
|
//TODO: Borrowed form BitcoinJ/Lighthouse. Remove Protos dependency, check complete code logic.
|
||||||
public class ScryptUtil {
|
public class ScryptUtil {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ScryptUtil.class);
|
private static final Logger log = LoggerFactory.getLogger(ScryptUtil.class);
|
||||||
|
@ -27,14 +30,31 @@ public class ScryptUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deriveKeyWithScrypt(KeyCrypterScrypt keyCrypterScrypt, String password, DeriveKeyResultHandler resultHandler) {
|
public static void deriveKeyWithScrypt(KeyCrypterScrypt keyCrypterScrypt, String password, DeriveKeyResultHandler resultHandler) {
|
||||||
new Thread(() -> {
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
log.info("Doing key derivation");
|
.setNameFormat("Routing-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
ExecutorService executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
|
||||||
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
executorService.submit(() -> {
|
||||||
long duration = System.currentTimeMillis() - start;
|
try {
|
||||||
log.info("Key derivation took {} msec", duration);
|
log.info("Doing key derivation");
|
||||||
UserThread.execute(() -> resultHandler.handleResult(aesKey));
|
long start = System.currentTimeMillis();
|
||||||
}).start();
|
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
||||||
|
long duration = System.currentTimeMillis() - start;
|
||||||
|
log.info("Key derivation took {} msec", duration);
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
try {
|
||||||
|
resultHandler.handleResult(aesKey);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package io.bitsquare.trade.offer;
|
package io.bitsquare.trade.offer;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import io.bitsquare.btc.TradeWalletService;
|
import io.bitsquare.btc.TradeWalletService;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
|
@ -47,10 +46,11 @@ 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.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.inject.internal.util.$Preconditions.checkNotNull;
|
import static com.google.inject.internal.util.$Preconditions.checkNotNull;
|
||||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ public class OpenOfferManager {
|
||||||
private boolean shutDownRequested;
|
private boolean shutDownRequested;
|
||||||
private ScheduledThreadPoolExecutor executor;
|
private ScheduledThreadPoolExecutor executor;
|
||||||
private P2PServiceListener p2PServiceListener;
|
private P2PServiceListener p2PServiceListener;
|
||||||
|
private final Timer timer = new Timer();
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -96,9 +96,13 @@ public class OpenOfferManager {
|
||||||
openOffersStorage = new Storage<>(storageDir);
|
openOffersStorage = new Storage<>(storageDir);
|
||||||
this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
|
this.openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
|
// In case the app did get killed the shutDown from the modules is not called, so we use a shutdown hook
|
||||||
Thread shutDownHookThread = new Thread(OpenOfferManager.this::shutDown, "OpenOfferManager.ShutDownHook");
|
Runtime.getRuntime().addShutdownHook(new Thread(OpenOfferManager.this::shutDown,
|
||||||
Runtime.getRuntime().addShutdownHook(shutDownHookThread);
|
"OpenOfferManager.ShutDownHook"));
|
||||||
|
|
||||||
// Handler for incoming offer availability requests
|
// Handler for incoming offer availability requests
|
||||||
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peerAddress) -> {
|
p2PService.addDecryptedMailListener((decryptedMessageWithPubKey, peerAddress) -> {
|
||||||
|
@ -155,25 +159,27 @@ public class OpenOfferManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startRePublishThread() {
|
private void startRePublishThread() {
|
||||||
if (p2PServiceListener != null) p2PService.removeP2PServiceListener(p2PServiceListener);
|
if (p2PServiceListener != null)
|
||||||
|
p2PService.removeP2PServiceListener(p2PServiceListener);
|
||||||
|
|
||||||
ThreadFactoryBuilder builder = new ThreadFactoryBuilder()
|
long period = (long) (Offer.TTL * 0.8);
|
||||||
.setDaemon(true)
|
TimerTask timerTask = new TimerTask() {
|
||||||
.setNameFormat("Re-publish offers thread")
|
@Override
|
||||||
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
public void run() {
|
||||||
|
Thread.currentThread().setName("RepublishOffers-%d");
|
||||||
// An executor that starts up threads when needed and shuts them down later.
|
rePublishOffers();
|
||||||
executor = new ScheduledThreadPoolExecutor(1, builder.build());
|
try {
|
||||||
executor.setKeepAliveTime(5, TimeUnit.SECONDS);
|
} catch (Throwable t) {
|
||||||
executor.allowCoreThreadTimeOut(true);
|
t.printStackTrace();
|
||||||
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
checkArgument(Offer.TTL > 120000, "Offer.TTL <= 120");
|
}
|
||||||
long period = Offer.TTL - 120000; // 2 min before TTL expires
|
};
|
||||||
executor.scheduleAtFixedRate(this::rePublishOffers, 500, period, TimeUnit.MILLISECONDS);
|
timer.scheduleAtFixedRate(timerTask, 500, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rePublishOffers() {
|
private void rePublishOffers() {
|
||||||
|
log.trace("rePublishOffers");
|
||||||
for (OpenOffer openOffer : openOffers) {
|
for (OpenOffer openOffer : openOffers) {
|
||||||
offerBookService.addOffer(openOffer.getOffer(),
|
offerBookService.addOffer(openOffer.getOffer(),
|
||||||
() -> log.debug("Successful added offer to P2P network"),
|
() -> log.debug("Successful added offer to P2P network"),
|
||||||
|
|
|
@ -51,9 +51,10 @@ public class SetupPayoutTxLockTimeReachedListener extends TradeTask {
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
log.debug("Block height reached " + blockHeightFuture.get().getHeight());
|
log.debug("Block height reached " + blockHeightFuture.get().getHeight());
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
broadcastTx();
|
broadcastTx();
|
||||||
},
|
},
|
||||||
|
|
|
@ -33,7 +33,6 @@ import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.DatePicker;
|
import javafx.scene.control.DatePicker;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import org.bitcoinj.core.Context;
|
|
||||||
import org.bitcoinj.core.Wallet;
|
import org.bitcoinj.core.Wallet;
|
||||||
import org.bitcoinj.crypto.KeyCrypter;
|
import org.bitcoinj.crypto.KeyCrypter;
|
||||||
import org.bitcoinj.crypto.MnemonicCode;
|
import org.bitcoinj.crypto.MnemonicCode;
|
||||||
|
@ -198,31 +197,27 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
||||||
log.info("Attempting wallet restore using seed '{}' from date {}", seedWordsTextArea.getText(), datePicker.getValue());
|
log.info("Attempting wallet restore using seed '{}' from date {}", seedWordsTextArea.getText(), datePicker.getValue());
|
||||||
long date = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
|
long date = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
|
||||||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
|
DeterministicSeed seed = new DeterministicSeed(Splitter.on(" ").splitToList(seedWordsTextArea.getText()), null, "", date);
|
||||||
Context ctx = Context.get();
|
walletService.restoreSeedWords(seed,
|
||||||
new Thread(() -> {
|
() -> UserThread.execute(() -> {
|
||||||
Context.propagate(ctx);
|
log.debug("Wallet restored with seed words");
|
||||||
walletService.restoreSeedWords(seed,
|
|
||||||
() -> UserThread.execute(() -> {
|
|
||||||
log.debug("Wallet restored with seed words");
|
|
||||||
|
|
||||||
new Popup()
|
new Popup()
|
||||||
.information("Wallet restored successfully with the new seed words.\n\n" +
|
.information("Wallet restored successfully with the new seed words.\n\n" +
|
||||||
"You need to restart now the application.")
|
"You need to restart now the application.")
|
||||||
.closeButtonText("Restart")
|
.closeButtonText("Restart")
|
||||||
.onClose(() -> BitsquareApp.restartDownHandler.run()).show();
|
.onClose(() -> BitsquareApp.restartDownHandler.run()).show();
|
||||||
}),
|
}),
|
||||||
throwable -> UserThread.execute(() -> {
|
throwable -> UserThread.execute(() -> {
|
||||||
log.error(throwable.getMessage());
|
log.error(throwable.getMessage());
|
||||||
new Popup()
|
new Popup()
|
||||||
.headLine("Wrong password")
|
.headLine("Wrong password")
|
||||||
.warning("Please try entering your password again, carefully checking for typos or spelling errors.")
|
.warning("Please try entering your password again, carefully checking for typos or spelling errors.")
|
||||||
.show();
|
.show();
|
||||||
|
|
||||||
new Popup()
|
new Popup()
|
||||||
.error("An error occurred when restoring the wallet with seed words.\n" +
|
.error("An error occurred when restoring the wallet with seed words.\n" +
|
||||||
"Error message: " + throwable.getMessage())
|
"Error message: " + throwable.getMessage())
|
||||||
.show();
|
.show();
|
||||||
}));
|
}));
|
||||||
}, "Restore wallet thread").start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package io.bitsquare.p2p;
|
||||||
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 com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
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.ProgramArguments;
|
import io.bitsquare.app.ProgramArguments;
|
||||||
|
@ -35,8 +36,7 @@ import java.io.File;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
@ -47,15 +47,20 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
public class P2PService {
|
public class P2PService {
|
||||||
private static final Logger log = LoggerFactory.getLogger(P2PService.class);
|
private static final Logger log = LoggerFactory.getLogger(P2PService.class);
|
||||||
|
|
||||||
|
private final SeedNodesRepository seedNodesRepository;
|
||||||
|
private final int port;
|
||||||
|
private final File torDir;
|
||||||
|
private final boolean useLocalhost;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final EncryptionService encryptionService;
|
private final EncryptionService encryptionService;
|
||||||
private final SetupListener setupListener;
|
private SetupListener setupListener;
|
||||||
private KeyRing keyRing;
|
private KeyRing keyRing;
|
||||||
|
private final File storageDir;
|
||||||
private final NetworkStatistics networkStatistics;
|
private final NetworkStatistics networkStatistics;
|
||||||
|
|
||||||
private final NetworkNode networkNode;
|
private NetworkNode networkNode;
|
||||||
private final Routing routing;
|
private Routing routing;
|
||||||
private final ProtectedExpirableDataStorage dataStorage;
|
private ProtectedExpirableDataStorage dataStorage;
|
||||||
private final List<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArrayList<>();
|
private final List<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArrayList<>();
|
||||||
private final List<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArrayList<>();
|
private final List<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArrayList<>();
|
||||||
private final List<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArrayList<>();
|
private final List<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArrayList<>();
|
||||||
|
@ -73,7 +78,7 @@ public class P2PService {
|
||||||
private boolean allSeedNodesRequested;
|
private boolean allSeedNodesRequested;
|
||||||
private Timer sendGetAllDataMessageTimer;
|
private Timer sendGetAllDataMessageTimer;
|
||||||
private volatile boolean hiddenServiceReady;
|
private volatile boolean hiddenServiceReady;
|
||||||
|
private final ExecutorService executorService;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -87,11 +92,27 @@ public class P2PService {
|
||||||
@Nullable EncryptionService encryptionService,
|
@Nullable EncryptionService encryptionService,
|
||||||
KeyRing keyRing,
|
KeyRing keyRing,
|
||||||
@Named("storage.dir") File storageDir) {
|
@Named("storage.dir") File storageDir) {
|
||||||
|
this.seedNodesRepository = seedNodesRepository;
|
||||||
|
this.port = port;
|
||||||
|
this.torDir = torDir;
|
||||||
|
this.useLocalhost = useLocalhost;
|
||||||
this.encryptionService = encryptionService;
|
this.encryptionService = encryptionService;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
|
this.storageDir = storageDir;
|
||||||
|
|
||||||
networkStatistics = new NetworkStatistics();
|
networkStatistics = new NetworkStatistics();
|
||||||
|
|
||||||
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("P2PService-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
// network layer
|
// network layer
|
||||||
if (useLocalhost) {
|
if (useLocalhost) {
|
||||||
networkNode = new LocalhostNetworkNode(port);
|
networkNode = new LocalhostNetworkNode(port);
|
||||||
|
@ -578,7 +599,12 @@ public class P2PService {
|
||||||
sendGetAllDataMessageTimer.schedule(new TimerTask() {
|
sendGetAllDataMessageTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
sendGetAllDataMessage(remainingSeedNodeAddresses);
|
try {
|
||||||
|
sendGetAllDataMessage(remainingSeedNodeAddresses);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, new Random().nextInt(2000) + 1000);
|
}, new Random().nextInt(2000) + 1000);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -37,11 +38,13 @@ public class Utils {
|
||||||
public static void shutDownExecutorService(ExecutorService executorService, long waitBeforeShutDown) {
|
public static void shutDownExecutorService(ExecutorService executorService, long waitBeforeShutDown) {
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
try {
|
try {
|
||||||
executorService.awaitTermination(waitBeforeShutDown, TimeUnit.MILLISECONDS);
|
boolean done = executorService.awaitTermination(waitBeforeShutDown, TimeUnit.MILLISECONDS);
|
||||||
|
if (!done) log.trace("Not all tasks completed at shutdown.");
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
executorService.shutdownNow();
|
final List<Runnable> rejected = executorService.shutdownNow();
|
||||||
|
log.debug("Rejected tasks: {}", rejected.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] compress(Serializable input) {
|
public static byte[] compress(Serializable input) {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package io.bitsquare.p2p.network;
|
package io.bitsquare.p2p.network;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.google.common.util.concurrent.Uninterruptibles;
|
||||||
import io.bitsquare.common.ByteArrayUtils;
|
import io.bitsquare.common.ByteArrayUtils;
|
||||||
import io.bitsquare.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.Utils;
|
import io.bitsquare.p2p.Utils;
|
||||||
import io.bitsquare.p2p.network.messages.CloseConnectionMessage;
|
import io.bitsquare.p2p.network.messages.CloseConnectionMessage;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -16,9 +19,7 @@ import java.net.SocketTimeoutException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
public class Connection {
|
public class Connection {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Connection.class);
|
private static final Logger log = LoggerFactory.getLogger(Connection.class);
|
||||||
|
@ -37,18 +38,18 @@ public class Connection {
|
||||||
private final String uid;
|
private final String uid;
|
||||||
|
|
||||||
private final Map<IllegalRequest, Integer> illegalRequests = new ConcurrentHashMap<>();
|
private final Map<IllegalRequest, Integer> illegalRequests = new ConcurrentHashMap<>();
|
||||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
private final ExecutorService executorService;
|
||||||
private ObjectOutputStream out;
|
private ObjectOutputStream out;
|
||||||
private ObjectInputStream in;
|
private ObjectInputStream in;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Address peerAddress;
|
||||||
|
private boolean isAuthenticated;
|
||||||
|
|
||||||
private volatile boolean stopped;
|
private volatile boolean stopped;
|
||||||
private volatile boolean shutDownInProgress;
|
private volatile boolean shutDownInProgress;
|
||||||
private volatile boolean inputHandlerStopped;
|
private volatile boolean inputHandlerStopped;
|
||||||
|
|
||||||
private volatile Date lastActivityDate;
|
private volatile Date lastActivityDate;
|
||||||
@Nullable
|
|
||||||
private Address peerAddress;
|
|
||||||
private boolean isAuthenticated;
|
|
||||||
|
|
||||||
|
|
||||||
//TODO got java.util.zip.DataFormatException: invalid distance too far back
|
//TODO got java.util.zip.DataFormatException: invalid distance too far back
|
||||||
|
@ -69,6 +70,17 @@ public class Connection {
|
||||||
|
|
||||||
uid = UUID.randomUUID().toString();
|
uid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("Connection-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
try {
|
try {
|
||||||
socket.setSoTimeout(SOCKET_TIMEOUT);
|
socket.setSoTimeout(SOCKET_TIMEOUT);
|
||||||
// Need to access first the ObjectOutputStream otherwise the ObjectInputStream would block
|
// Need to access first the ObjectOutputStream otherwise the ObjectInputStream would block
|
||||||
|
@ -203,12 +215,8 @@ public class Connection {
|
||||||
|
|
||||||
if (sendCloseConnectionMessage) {
|
if (sendCloseConnectionMessage) {
|
||||||
sendMessage(new CloseConnectionMessage());
|
sendMessage(new CloseConnectionMessage());
|
||||||
try {
|
// give a bit of time for closing gracefully
|
||||||
// give a bit of time for closing gracefully
|
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(100);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -296,61 +304,79 @@ public class Connection {
|
||||||
private class InputHandler implements Runnable {
|
private class InputHandler implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Thread.currentThread().setName("InputHandler-" + socket.getLocalPort());
|
try {
|
||||||
while (!inputHandlerStopped) {
|
Thread.currentThread().setName("InputHandler-" + socket.getLocalPort());
|
||||||
try {
|
while (!inputHandlerStopped) {
|
||||||
log.trace("InputHandler waiting for incoming messages connection=" + Connection.this.getObjectId());
|
try {
|
||||||
Object rawInputObject = in.readObject();
|
log.trace("InputHandler waiting for incoming messages connection=" + Connection.this.getObjectId());
|
||||||
log.trace("New data arrived at inputHandler of connection=" + Connection.this.toString()
|
Object rawInputObject = in.readObject();
|
||||||
+ " rawInputObject " + rawInputObject);
|
log.trace("New data arrived at inputHandler of connection=" + Connection.this.toString()
|
||||||
|
+ " rawInputObject " + rawInputObject);
|
||||||
|
|
||||||
int size = ByteArrayUtils.objectToByteArray(rawInputObject).length;
|
int size = ByteArrayUtils.objectToByteArray(rawInputObject).length;
|
||||||
if (size <= MAX_MSG_SIZE) {
|
|
||||||
Serializable serializable = null;
|
|
||||||
if (useCompression) {
|
|
||||||
if (rawInputObject instanceof byte[]) {
|
|
||||||
byte[] compressedObjectAsBytes = (byte[]) rawInputObject;
|
|
||||||
size = compressedObjectAsBytes.length;
|
|
||||||
//log.trace("Read object compressed data size: " + size);
|
|
||||||
serializable = Utils.decompress(compressedObjectAsBytes);
|
|
||||||
} else {
|
|
||||||
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (rawInputObject instanceof Serializable) {
|
|
||||||
serializable = (Serializable) rawInputObject;
|
|
||||||
} else {
|
|
||||||
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//log.trace("Read object decompressed data size: " + ByteArrayUtils.objectToByteArray(serializable).length);
|
|
||||||
|
|
||||||
// compressed size might be bigger theoretically so we check again after decompression
|
|
||||||
if (size <= MAX_MSG_SIZE) {
|
if (size <= MAX_MSG_SIZE) {
|
||||||
if (serializable instanceof Message) {
|
Serializable serializable = null;
|
||||||
lastActivityDate = new Date();
|
if (useCompression) {
|
||||||
Message message = (Message) serializable;
|
if (rawInputObject instanceof byte[]) {
|
||||||
if (message instanceof CloseConnectionMessage) {
|
byte[] compressedObjectAsBytes = (byte[]) rawInputObject;
|
||||||
inputHandlerStopped = true;
|
size = compressedObjectAsBytes.length;
|
||||||
shutDown(false);
|
//log.trace("Read object compressed data size: " + size);
|
||||||
|
serializable = Utils.decompress(compressedObjectAsBytes);
|
||||||
} else {
|
} else {
|
||||||
executorService.submit(() -> messageListener.onMessage(message, Connection.this));
|
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
if (rawInputObject instanceof Serializable) {
|
||||||
|
serializable = (Serializable) rawInputObject;
|
||||||
|
} else {
|
||||||
|
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log.trace("Read object decompressed data size: " + ByteArrayUtils.objectToByteArray(serializable).length);
|
||||||
|
|
||||||
|
// compressed size might be bigger theoretically so we check again after decompression
|
||||||
|
if (size <= MAX_MSG_SIZE) {
|
||||||
|
if (serializable instanceof Message) {
|
||||||
|
lastActivityDate = new Date();
|
||||||
|
Message message = (Message) serializable;
|
||||||
|
if (message instanceof CloseConnectionMessage) {
|
||||||
|
inputHandlerStopped = true;
|
||||||
|
shutDown(false);
|
||||||
|
} else {
|
||||||
|
Task task = new Task() {
|
||||||
|
@Override
|
||||||
|
protected Object call() throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
executorService.submit(() -> {
|
||||||
|
try {
|
||||||
|
messageListener.onMessage(message, Connection.this);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reportIllegalRequest(IllegalRequest.InvalidDataType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("Received decompressed data exceeds max. msg size.");
|
||||||
|
reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("Received decompressed data exceeds max. msg size.");
|
log.error("Received compressed data exceeds max. msg size.");
|
||||||
reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
|
reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
log.error("Received compressed data exceeds max. msg size.");
|
inputHandlerStopped = true;
|
||||||
reportIllegalRequest(IllegalRequest.MaxSizeExceeded);
|
handleConnectionException(e);
|
||||||
}
|
}
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
|
||||||
inputHandlerStopped = true;
|
|
||||||
handleConnectionException(e);
|
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package io.bitsquare.p2p.network;
|
package io.bitsquare.p2p.network;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.*;
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
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.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
|
@ -19,6 +16,7 @@ import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class LocalhostNetworkNode extends NetworkNode {
|
public class LocalhostNetworkNode extends NetworkNode {
|
||||||
|
@ -92,8 +90,9 @@ public class LocalhostNetworkNode extends NetworkNode {
|
||||||
private void createTorNode(final Consumer<TorNode> resultHandler) {
|
private void createTorNode(final Consumer<TorNode> resultHandler) {
|
||||||
Callable<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> task = () -> {
|
Callable<TorNode<JavaOnionProxyManager, JavaOnionProxyContext>> task = () -> {
|
||||||
long ts = System.currentTimeMillis();
|
long ts = System.currentTimeMillis();
|
||||||
log.trace("[simulation] Create TorNode");
|
if (simulateTorDelayTorNode > 0)
|
||||||
if (simulateTorDelayTorNode > 0) Thread.sleep(simulateTorDelayTorNode);
|
Uninterruptibles.sleepUninterruptibly(simulateTorDelayTorNode, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
log.info("\n\n############################################################\n" +
|
log.info("\n\n############################################################\n" +
|
||||||
"TorNode created [simulation]:" +
|
"TorNode created [simulation]:" +
|
||||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||||
|
@ -115,8 +114,9 @@ public class LocalhostNetworkNode extends NetworkNode {
|
||||||
private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) {
|
private void createHiddenService(final Consumer<HiddenServiceDescriptor> resultHandler) {
|
||||||
Callable<HiddenServiceDescriptor> task = () -> {
|
Callable<HiddenServiceDescriptor> task = () -> {
|
||||||
long ts = System.currentTimeMillis();
|
long ts = System.currentTimeMillis();
|
||||||
log.debug("[simulation] Create hidden service");
|
if (simulateTorDelayHiddenService > 0)
|
||||||
if (simulateTorDelayHiddenService > 0) Thread.sleep(simulateTorDelayHiddenService);
|
Uninterruptibles.sleepUninterruptibly(simulateTorDelayHiddenService, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
log.info("\n\n############################################################\n" +
|
log.info("\n\n############################################################\n" +
|
||||||
"Hidden service created [simulation]:" +
|
"Hidden service created [simulation]:" +
|
||||||
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
"\nTook " + (System.currentTimeMillis() - ts) + " ms"
|
||||||
|
|
|
@ -58,71 +58,77 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
final SettableFuture<Connection> resultFuture = SettableFuture.create();
|
final SettableFuture<Connection> resultFuture = SettableFuture.create();
|
||||||
|
|
||||||
Callable<Connection> task = () -> {
|
Callable<Connection> task = () -> {
|
||||||
Thread.currentThread().setName("Outgoing-connection-to-" + peerAddress);
|
try {
|
||||||
|
Thread.currentThread().setName("Outgoing-connection-to-" + peerAddress);
|
||||||
|
|
||||||
Optional<Connection> outboundConnectionOptional = getOutboundConnection(peerAddress);
|
Optional<Connection> outboundConnectionOptional = getOutboundConnection(peerAddress);
|
||||||
Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null;
|
Connection connection = outboundConnectionOptional.isPresent() ? outboundConnectionOptional.get() : null;
|
||||||
|
|
||||||
if (connection != null && connection.isStopped()) {
|
if (connection != null && connection.isStopped()) {
|
||||||
log.trace("We have a connection which is already stopped in outBoundConnections. Connection.uid=" + connection.getUid());
|
log.trace("We have a connection which is already stopped in outBoundConnections. Connection.uid=" + connection.getUid());
|
||||||
outBoundConnections.remove(connection);
|
outBoundConnections.remove(connection);
|
||||||
connection = null;
|
connection = null;
|
||||||
}
|
|
||||||
|
|
||||||
if (connection == null) {
|
|
||||||
Optional<Connection> inboundConnectionOptional = getInboundConnection(peerAddress);
|
|
||||||
if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get();
|
|
||||||
if (connection != null)
|
|
||||||
log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection == null) {
|
|
||||||
try {
|
|
||||||
Socket socket = getSocket(peerAddress); // can take a while when using tor
|
|
||||||
connection = new Connection(socket,
|
|
||||||
(message1, connection1) -> NetworkNode.this.onMessage(message1, connection1),
|
|
||||||
new ConnectionListener() {
|
|
||||||
@Override
|
|
||||||
public void onConnection(Connection connection) {
|
|
||||||
NetworkNode.this.onConnection(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
|
|
||||||
NetworkNode.this.onPeerAddressAuthenticated(peerAddress, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisconnect(Reason reason, Connection connection) {
|
|
||||||
log.trace("onDisconnect at outgoing connection to peerAddress " + peerAddress);
|
|
||||||
NetworkNode.this.onDisconnect(reason, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(Throwable throwable) {
|
|
||||||
NetworkNode.this.onError(throwable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!outBoundConnections.contains(connection))
|
|
||||||
outBoundConnections.add(connection);
|
|
||||||
else
|
|
||||||
log.error("We have already that connection in our list. That must not happen. "
|
|
||||||
+ outBoundConnections + " / connection=" + connection);
|
|
||||||
|
|
||||||
log.info("\n\nNetworkNode created new outbound connection:"
|
|
||||||
+ "\npeerAddress=" + peerAddress.port
|
|
||||||
+ "\nconnection.uid=" + connection.getUid()
|
|
||||||
+ "\nmessage=" + message
|
|
||||||
+ "\n\n");
|
|
||||||
} catch (Throwable t) {
|
|
||||||
resultFuture.setException(t);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (connection == null) {
|
||||||
|
Optional<Connection> inboundConnectionOptional = getInboundConnection(peerAddress);
|
||||||
|
if (inboundConnectionOptional.isPresent()) connection = inboundConnectionOptional.get();
|
||||||
|
if (connection != null)
|
||||||
|
log.trace("We have found a connection in inBoundConnections. Connection.uid=" + connection.getUid());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection == null) {
|
||||||
|
try {
|
||||||
|
Socket socket = getSocket(peerAddress); // can take a while when using tor
|
||||||
|
connection = new Connection(socket,
|
||||||
|
(message1, connection1) -> NetworkNode.this.onMessage(message1, connection1),
|
||||||
|
new ConnectionListener() {
|
||||||
|
@Override
|
||||||
|
public void onConnection(Connection connection) {
|
||||||
|
NetworkNode.this.onConnection(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerAddressAuthenticated(Address peerAddress, Connection connection) {
|
||||||
|
NetworkNode.this.onPeerAddressAuthenticated(peerAddress, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect(Reason reason, Connection connection) {
|
||||||
|
log.trace("onDisconnect at outgoing connection to peerAddress " + peerAddress);
|
||||||
|
NetworkNode.this.onDisconnect(reason, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable throwable) {
|
||||||
|
NetworkNode.this.onError(throwable);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!outBoundConnections.contains(connection))
|
||||||
|
outBoundConnections.add(connection);
|
||||||
|
else
|
||||||
|
log.error("We have already that connection in our list. That must not happen. "
|
||||||
|
+ outBoundConnections + " / connection=" + connection);
|
||||||
|
|
||||||
|
log.info("\n\nNetworkNode created new outbound connection:"
|
||||||
|
+ "\npeerAddress=" + peerAddress.port
|
||||||
|
+ "\nconnection.uid=" + connection.getUid()
|
||||||
|
+ "\nmessage=" + message
|
||||||
|
+ "\n\n");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
resultFuture.setException(t);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.sendMessage(message);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
throw t;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.sendMessage(message);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ListenableFuture<Connection> future = executorService.submit(task);
|
ListenableFuture<Connection> future = executorService.submit(task);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package io.bitsquare.p2p.network;
|
package io.bitsquare.p2p.network;
|
||||||
|
|
||||||
import io.bitsquare.p2p.Utils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -10,8 +9,6 @@ import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
public class Server implements Runnable {
|
public class Server implements Runnable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Server.class);
|
private static final Logger log = LoggerFactory.getLogger(Server.class);
|
||||||
|
@ -19,7 +16,6 @@ public class Server implements Runnable {
|
||||||
private final ServerSocket serverSocket;
|
private final ServerSocket serverSocket;
|
||||||
private final MessageListener messageListener;
|
private final MessageListener messageListener;
|
||||||
private final ConnectionListener connectionListener;
|
private final ConnectionListener connectionListener;
|
||||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
|
||||||
private final List<Connection> connections = new CopyOnWriteArrayList<>();
|
private final List<Connection> connections = new CopyOnWriteArrayList<>();
|
||||||
private volatile boolean stopped;
|
private volatile boolean stopped;
|
||||||
|
|
||||||
|
@ -32,25 +28,30 @@ public class Server implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Thread.currentThread().setName("Server-" + serverSocket.getLocalPort());
|
try {
|
||||||
while (!stopped) {
|
Thread.currentThread().setName("Server-" + serverSocket.getLocalPort());
|
||||||
try {
|
while (!stopped) {
|
||||||
log.info("Ready to accept new clients on port " + serverSocket.getLocalPort());
|
try {
|
||||||
final Socket socket = serverSocket.accept();
|
log.info("Ready to accept new clients on port " + serverSocket.getLocalPort());
|
||||||
log.info("Accepted new client on port " + socket.getLocalPort());
|
final Socket socket = serverSocket.accept();
|
||||||
Connection connection = new Connection(socket, messageListener, connectionListener);
|
log.info("Accepted new client on port " + socket.getLocalPort());
|
||||||
log.info("\n\nServer created new inbound connection:"
|
Connection connection = new Connection(socket, messageListener, connectionListener);
|
||||||
+ "\nserverSocket.getLocalPort()=" + serverSocket.getLocalPort()
|
log.info("\n\nServer created new inbound connection:"
|
||||||
+ "\nsocket.getPort()=" + socket.getPort()
|
+ "\nserverSocket.getLocalPort()=" + serverSocket.getLocalPort()
|
||||||
+ "\nconnection.uid=" + connection.getUid()
|
+ "\nsocket.getPort()=" + socket.getPort()
|
||||||
+ "\n\n");
|
+ "\nconnection.uid=" + connection.getUid()
|
||||||
|
+ "\n\n");
|
||||||
|
|
||||||
log.info("Server created new socket with port " + socket.getPort());
|
log.info("Server created new socket with port " + socket.getPort());
|
||||||
connections.add(connection);
|
connections.add(connection);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (!stopped)
|
if (!stopped)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +68,6 @@ public class Server implements Runnable {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
Utils.shutDownExecutorService(executorService);
|
|
||||||
log.debug("Server shutdown complete");
|
log.debug("Server shutdown complete");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -60,48 +61,62 @@ public class TorNetworkNode extends NetworkNode {
|
||||||
|
|
||||||
this.torDir = torDir;
|
this.torDir = torDir;
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
selfTestTimeoutTask = new TimerTask() {
|
selfTestTimeoutTask = new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
log.error("A timeout occurred at self test");
|
try {
|
||||||
stopSelfTestTimer();
|
log.error("A timeout occurred at self test");
|
||||||
selfTestFailed();
|
stopSelfTestTimer();
|
||||||
|
selfTestFailed();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
selfTestTask = new TimerTask() {
|
selfTestTask = new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
stopTimeoutTimer();
|
try {
|
||||||
if (selfTestRunning.get()) {
|
stopTimeoutTimer();
|
||||||
log.debug("running self test");
|
|
||||||
selfTestTimeoutTimer = new Timer();
|
|
||||||
selfTestTimeoutTimer.schedule(selfTestTimeoutTask, TIMEOUT);
|
|
||||||
// might be interrupted by timeout task
|
|
||||||
if (selfTestRunning.get()) {
|
if (selfTestRunning.get()) {
|
||||||
nonce = random.nextLong();
|
log.debug("running self test");
|
||||||
log.trace("send msg with nonce " + nonce);
|
selfTestTimeoutTimer = new Timer();
|
||||||
|
selfTestTimeoutTimer.schedule(selfTestTimeoutTask, TIMEOUT);
|
||||||
|
// might be interrupted by timeout task
|
||||||
|
if (selfTestRunning.get()) {
|
||||||
|
nonce = random.nextLong();
|
||||||
|
log.trace("send msg with nonce " + nonce);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SettableFuture<Connection> future = sendMessage(new Address(hiddenServiceDescriptor.getFullAddress()), new SelfTestMessage(nonce));
|
SettableFuture<Connection> future = sendMessage(new Address(hiddenServiceDescriptor.getFullAddress()), new SelfTestMessage(nonce));
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
log.trace("Sending self test message succeeded");
|
log.trace("Sending self test message succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Throwable throwable) {
|
public void onFailure(Throwable throwable) {
|
||||||
log.error("Error at sending self test message. Exception = " + throwable);
|
log.error("Error at sending self test message. Exception = " + throwable);
|
||||||
stopTimeoutTimer();
|
stopTimeoutTimer();
|
||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
selfTestFailed();
|
selfTestFailed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -144,11 +159,7 @@ public class TorNetworkNode extends NetworkNode {
|
||||||
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
|
TorNetworkNode.this.hiddenServiceDescriptor = hiddenServiceDescriptor;
|
||||||
|
|
||||||
startServer(hiddenServiceDescriptor.getServerSocket());
|
startServer(hiddenServiceDescriptor.getServerSocket());
|
||||||
try {
|
Uninterruptibles.sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(500);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
setupListeners.stream().forEach(e -> e.onHiddenServiceReady());
|
setupListeners.stream().forEach(e -> e.onHiddenServiceReady());
|
||||||
|
|
||||||
|
@ -252,12 +263,7 @@ public class TorNetworkNode extends NetworkNode {
|
||||||
restartCounter++;
|
restartCounter++;
|
||||||
if (restartCounter <= MAX_RESTART_ATTEMPTS) {
|
if (restartCounter <= MAX_RESTART_ATTEMPTS) {
|
||||||
shutDown(() -> {
|
shutDown(() -> {
|
||||||
try {
|
Uninterruptibles.sleepUninterruptibly(WAIT_BEFORE_RESTART, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(WAIT_BEFORE_RESTART);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
log.warn("We restart tor as too many self tests failed.");
|
log.warn("We restart tor as too many self tests failed.");
|
||||||
start(null);
|
start(null);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package io.bitsquare.p2p.routing;
|
package io.bitsquare.p2p.routing;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.*;
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.p2p.Utils;
|
import io.bitsquare.p2p.Utils;
|
||||||
|
@ -15,10 +13,7 @@ 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.*;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Routing {
|
public class Routing {
|
||||||
|
@ -47,7 +42,7 @@ public class Routing {
|
||||||
private final List<Address> reportedNeighborAddresses = new CopyOnWriteArrayList<>();
|
private final List<Address> reportedNeighborAddresses = new CopyOnWriteArrayList<>();
|
||||||
private final Map<Address, Runnable> authenticationCompleteHandlers = new ConcurrentHashMap<>();
|
private final Map<Address, Runnable> authenticationCompleteHandlers = new ConcurrentHashMap<>();
|
||||||
private final Timer maintenanceTimer = new Timer();
|
private final Timer maintenanceTimer = new Timer();
|
||||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
private final ExecutorService executorService;
|
||||||
private volatile boolean shutDownInProgress;
|
private volatile boolean shutDownInProgress;
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +56,17 @@ public class Routing {
|
||||||
// We copy it as we remove ourselves later from the list if we are a seed node
|
// We copy it as we remove ourselves later from the list if we are a seed node
|
||||||
this.seedNodes = new CopyOnWriteArrayList<>(seeds);
|
this.seedNodes = new CopyOnWriteArrayList<>(seeds);
|
||||||
|
|
||||||
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("Routing-%d")
|
||||||
|
.setDaemon(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
executorService = new ThreadPoolExecutor(5, 50, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), threadFactory);
|
||||||
|
|
||||||
|
init(networkNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(NetworkNode networkNode) {
|
||||||
networkNode.addMessageListener((message, connection) -> {
|
networkNode.addMessageListener((message, connection) -> {
|
||||||
if (message instanceof AuthenticationMessage)
|
if (message instanceof AuthenticationMessage)
|
||||||
processAuthenticationMessage((AuthenticationMessage) message, connection);
|
processAuthenticationMessage((AuthenticationMessage) message, connection);
|
||||||
|
@ -110,8 +116,13 @@ public class Routing {
|
||||||
maintenanceTimer.scheduleAtFixedRate(new TimerTask() {
|
maintenanceTimer.scheduleAtFixedRate(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
disconnectOldConnections();
|
try {
|
||||||
pingNeighbors();
|
disconnectOldConnections();
|
||||||
|
pingNeighbors();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, MAINTENANCE_INTERVAL, MAINTENANCE_INTERVAL);
|
}, MAINTENANCE_INTERVAL, MAINTENANCE_INTERVAL);
|
||||||
}
|
}
|
||||||
|
@ -126,11 +137,7 @@ public class Routing {
|
||||||
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("Shutdown oldest connection with last activity date=" + connection.getLastActivityDate() + " / connection=" + connection);
|
||||||
connection.shutDown(() -> disconnectOldConnections());
|
connection.shutDown(() -> disconnectOldConnections());
|
||||||
try {
|
Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(200);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,11 +160,7 @@ public class Routing {
|
||||||
removeNeighbor(e.address);
|
removeNeighbor(e.address);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
Uninterruptibles.sleepUninterruptibly(new Random().nextInt(5000) + 5000, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(new Random().nextInt(5000) + 5000);
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,16 +250,16 @@ public class Routing {
|
||||||
public void startAuthentication(List<Address> connectedSeedNodes) {
|
public void startAuthentication(List<Address> connectedSeedNodes) {
|
||||||
connectedSeedNodes.forEach(connectedSeedNode -> {
|
connectedSeedNodes.forEach(connectedSeedNode -> {
|
||||||
executorService.submit(() -> {
|
executorService.submit(() -> {
|
||||||
sendRequestAuthenticationMessage(seedNodes, connectedSeedNode);
|
|
||||||
try {
|
try {
|
||||||
|
sendRequestAuthenticationMessage(seedNodes, connectedSeedNode);
|
||||||
// give a random pause of 3-5 sec. before using the next
|
// give a random pause of 3-5 sec. before using the next
|
||||||
Thread.sleep(new Random().nextInt(2000) + 3000);
|
Uninterruptibles.sleepUninterruptibly(new Random().nextInt(2000) + 3000, TimeUnit.MILLISECONDS);
|
||||||
} catch (InterruptedException ignored) {
|
} catch (Throwable t) {
|
||||||
Thread.currentThread().interrupt();
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendRequestAuthenticationMessage(final List<Address> remainingSeedNodes, final Address address) {
|
private void sendRequestAuthenticationMessage(final List<Address> remainingSeedNodes, final Address address) {
|
||||||
|
@ -315,17 +318,11 @@ public class Routing {
|
||||||
connection.shutDown(() -> {
|
connection.shutDown(() -> {
|
||||||
// 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)
|
||||||
try {
|
Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
|
||||||
Thread.sleep(100);
|
|
||||||
} catch (InterruptedException e) {
|
if (simulateAuthTorNode > 0)
|
||||||
Thread.currentThread().interrupt();
|
Uninterruptibles.sleepUninterruptibly(simulateAuthTorNode, TimeUnit.MILLISECONDS);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (simulateAuthTorNode > 0) Thread.sleep(simulateAuthTorNode);
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + getAddress());
|
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + getAddress());
|
||||||
long nonce = addToMapAndGetNonce(peerAddress);
|
long nonce = addToMapAndGetNonce(peerAddress);
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new ChallengeMessage(getAddress(), requestAuthenticationMessage.nonce, nonce));
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new ChallengeMessage(getAddress(), requestAuthenticationMessage.nonce, nonce));
|
||||||
|
@ -413,25 +410,25 @@ public class Routing {
|
||||||
ArrayList<Address> neighborAddresses = ((GetNeighborsMessage) message).neighborAddresses;
|
ArrayList<Address> neighborAddresses = ((GetNeighborsMessage) message).neighborAddresses;
|
||||||
log.trace("Received neighbors: " + neighborAddresses);
|
log.trace("Received neighbors: " + neighborAddresses);
|
||||||
// remove ourselves
|
// remove ourselves
|
||||||
neighborAddresses.remove(getAddress());
|
|
||||||
addToReportedNeighbors(neighborAddresses, connection);
|
addToReportedNeighbors(neighborAddresses, connection);
|
||||||
}
|
}
|
||||||
} else if (message instanceof NeighborsMessage) {
|
} else if (message instanceof NeighborsMessage) {
|
||||||
log.trace("NeighborsMessage from " + connection.getPeerAddress() + " at " + getAddress());
|
|
||||||
NeighborsMessage neighborsMessage = (NeighborsMessage) message;
|
NeighborsMessage neighborsMessage = (NeighborsMessage) message;
|
||||||
|
Address peerAddress = neighborsMessage.address;
|
||||||
|
log.trace("NeighborsMessage from " + peerAddress + " at " + getAddress());
|
||||||
ArrayList<Address> neighborAddresses = neighborsMessage.neighborAddresses;
|
ArrayList<Address> neighborAddresses = neighborsMessage.neighborAddresses;
|
||||||
log.trace("Received neighbors: " + neighborAddresses);
|
log.trace("Received neighbors: " + neighborAddresses);
|
||||||
// remove ourselves
|
// remove ourselves
|
||||||
neighborAddresses.remove(getAddress());
|
|
||||||
addToReportedNeighbors(neighborAddresses, connection);
|
addToReportedNeighbors(neighborAddresses, connection);
|
||||||
|
|
||||||
log.info("\n\nAuthenticationComplete\nPeer with address " + connection.getPeerAddress().toString()
|
|
||||||
+ " authenticated (" + connection.getObjectId() + "). Took "
|
|
||||||
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
|
|
||||||
|
|
||||||
// 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
|
||||||
setAuthenticated(connection, neighborsMessage.address);
|
|
||||||
|
log.info("\n\nAuthenticationComplete\nPeer with address " + peerAddress
|
||||||
|
+ " authenticated (" + connection.getObjectId() + "). Took "
|
||||||
|
+ (System.currentTimeMillis() - startAuthTs) + " ms. \n\n");
|
||||||
|
|
||||||
|
setAuthenticated(connection, peerAddress);
|
||||||
|
|
||||||
Runnable authenticationCompleteHandler = authenticationCompleteHandlers.remove(connection.getPeerAddress());
|
Runnable authenticationCompleteHandler = authenticationCompleteHandlers.remove(connection.getPeerAddress());
|
||||||
if (authenticationCompleteHandler != null)
|
if (authenticationCompleteHandler != null)
|
||||||
|
@ -442,18 +439,21 @@ public class Routing {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToReportedNeighbors(ArrayList<Address> neighborAddresses, Connection connection) {
|
private void addToReportedNeighbors(ArrayList<Address> neighborAddresses, Connection connection) {
|
||||||
|
log.trace("addToReportedNeighbors");
|
||||||
// we disconnect misbehaving nodes trying to send too many neighbors
|
// we disconnect misbehaving nodes trying to send too many neighbors
|
||||||
// reported neighbors include the peers connected neighbors which is normally max. 8 but we give some headroom
|
// reported neighbors include the peers connected neighbors which is normally max. 8 but we give some headroom
|
||||||
// for safety
|
// for safety
|
||||||
if (neighborAddresses.size() > 1100) {
|
if (neighborAddresses.size() > 1100) {
|
||||||
connection.shutDown();
|
connection.shutDown();
|
||||||
} else {
|
} else {
|
||||||
|
neighborAddresses.remove(getAddress());
|
||||||
reportedNeighborAddresses.addAll(neighborAddresses);
|
reportedNeighborAddresses.addAll(neighborAddresses);
|
||||||
purgeReportedNeighbors();
|
purgeReportedNeighbors();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void purgeReportedNeighbors() {
|
private void purgeReportedNeighbors() {
|
||||||
|
log.trace("purgeReportedNeighbors");
|
||||||
int all = getAllNeighborAddresses().size();
|
int all = getAllNeighborAddresses().size();
|
||||||
if (all > 1000) {
|
if (all > 1000) {
|
||||||
int diff = all - 100;
|
int diff = all - 100;
|
||||||
|
@ -478,20 +478,21 @@ public class Routing {
|
||||||
private void authenticateToNextRandomNeighbor() {
|
private void authenticateToNextRandomNeighbor() {
|
||||||
executorService.submit(() -> {
|
executorService.submit(() -> {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(new Random().nextInt(200) + 200);
|
Uninterruptibles.sleepUninterruptibly(new Random().nextInt(200) + 200, TimeUnit.MILLISECONDS);
|
||||||
} catch (InterruptedException ignored) {
|
if (getConnectedNeighbors().size() <= MAX_CONNECTIONS) {
|
||||||
Thread.currentThread().interrupt();
|
Address randomNotConnectedNeighborAddress = getRandomNotConnectedNeighborAddress();
|
||||||
}
|
if (randomNotConnectedNeighborAddress != null) {
|
||||||
if (getConnectedNeighbors().size() <= MAX_CONNECTIONS) {
|
log.info("We try to build an authenticated connection to a random neighbor. " + randomNotConnectedNeighborAddress);
|
||||||
Address randomNotConnectedNeighborAddress = getRandomNotConnectedNeighborAddress();
|
authenticateToPeer(randomNotConnectedNeighborAddress, null, () -> authenticateToNextRandomNeighbor());
|
||||||
if (randomNotConnectedNeighborAddress != null) {
|
} else {
|
||||||
log.info("We try to build an authenticated connection to a random neighbor. " + randomNotConnectedNeighborAddress);
|
log.info("No more neighbors available for connecting.");
|
||||||
authenticateToPeer(randomNotConnectedNeighborAddress, null, () -> authenticateToNextRandomNeighbor());
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("No more neighbors available for connecting.");
|
log.info("We have already enough connections.");
|
||||||
}
|
}
|
||||||
} else {
|
} catch (Throwable t) {
|
||||||
log.info("We have already enough connections.");
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -531,9 +532,8 @@ public class Routing {
|
||||||
|
|
||||||
private boolean verifyNonceAndAuthenticatePeerAddress(long peersNonce, Address peerAddress) {
|
private boolean verifyNonceAndAuthenticatePeerAddress(long peersNonce, Address peerAddress) {
|
||||||
log.trace("verifyNonceAndAuthenticatePeerAddress nonceMap=" + nonceMap + " / peerAddress=" + peerAddress);
|
log.trace("verifyNonceAndAuthenticatePeerAddress nonceMap=" + nonceMap + " / peerAddress=" + peerAddress);
|
||||||
long nonce = nonceMap.remove(peerAddress);
|
Long nonce = nonceMap.remove(peerAddress);
|
||||||
boolean result = nonce == peersNonce;
|
return nonce != null && nonce == peersNonce;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAuthenticated(Connection connection, Address peerAddress) {
|
private void setAuthenticated(Connection connection, Address peerAddress) {
|
||||||
|
|
|
@ -52,6 +52,10 @@ public class ProtectedExpirableDataStorage {
|
||||||
|
|
||||||
storage = new Storage<>(storageDir);
|
storage = new Storage<>(storageDir);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
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;
|
||||||
|
@ -79,9 +83,14 @@ public class ProtectedExpirableDataStorage {
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
log.info("removeExpiredEntries called ");
|
try {
|
||||||
map.entrySet().stream().filter(entry -> entry.getValue().isExpired())
|
log.info("removeExpiredEntries called ");
|
||||||
.forEach(entry -> map.remove(entry.getKey()));
|
map.entrySet().stream().filter(entry -> entry.getValue().isExpired())
|
||||||
|
.forEach(entry -> map.remove(entry.getKey()));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CHECK_TTL_INTERVAL,
|
CHECK_TTL_INTERVAL,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue