incease fee as testnet is spammed with tx with high fees. use FxTimer in fx classes (gui). Use scheduledExecutor instead of timer

This commit is contained in:
Manfred Karrer 2016-01-01 19:34:00 +01:00
parent 8716a506e8
commit e456883f9b
19 changed files with 176 additions and 175 deletions

View file

@ -50,6 +50,7 @@ public class UserThread {
} }
// Prefer FxTimer if a delay is needed in a JavaFx class (gui module)
public static Timer runAfterRandomDelay(Runnable runnable, long minDelayInSec, long maxDelayInSec) { public static Timer runAfterRandomDelay(Runnable runnable, long minDelayInSec, long maxDelayInSec) {
return UserThread.runAfterRandomDelay(runnable, minDelayInSec, maxDelayInSec, TimeUnit.SECONDS); return UserThread.runAfterRandomDelay(runnable, minDelayInSec, maxDelayInSec, TimeUnit.SECONDS);
} }

View file

@ -35,10 +35,7 @@ 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.Random;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.*;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/** /**
@ -68,16 +65,35 @@ public class Utilities {
public static ThreadPoolExecutor getThreadPoolExecutor(String name, public static ThreadPoolExecutor getThreadPoolExecutor(String name,
int corePoolSize, int corePoolSize,
int maximumPoolSize, int maximumPoolSize,
long keepAliveTime) { long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder() final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name) .setNameFormat(name)
.setDaemon(true) .setDaemon(true)
.build(); .build();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory); TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true); executor.allowCoreThreadTimeOut(true);
threadPoolExecutor.setRejectedExecutionHandler((r, executor) -> log.warn("RejectedExecutionHandler called")); executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
return threadPoolExecutor; return executor;
}
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor(String name,
int corePoolSize,
int maximumPoolSize,
long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name)
.setDaemon(true)
.setPriority(Thread.MIN_PRIORITY)
.build();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true);
executor.setMaximumPoolSize(maximumPoolSize);
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
return executor;
} }
public static boolean isUnix() { public static boolean isUnix() {

View file

@ -36,8 +36,8 @@ package io.bitsquare.storage;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -80,17 +80,7 @@ public class FileManager<T> {
this.dir = dir; this.dir = dir;
this.storageFile = storageFile; this.storageFile = storageFile;
ThreadFactoryBuilder builder = new ThreadFactoryBuilder() executor = Utilities.getScheduledThreadPoolExecutor("FileManager", 1, 10, 5);
.setDaemon(true)
.setNameFormat("FileManager-%d")
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
// An executor that starts up threads when needed and shuts them down later.
executor = new ScheduledThreadPoolExecutor(1, builder.build());
executor.setKeepAliveTime(5, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true);
executor.setMaximumPoolSize(10);
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access. // File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
savePending = new AtomicBoolean(); savePending = new AtomicBoolean();
@ -177,14 +167,8 @@ public class FileManager<T> {
* Shut down auto-saving. * Shut down auto-saving.
*/ */
public void shutDown() { public void shutDown() {
/* if (serializable != null)
log.debug("shutDown " + serializable.getClass().getSimpleName());
else
log.debug("shutDown");*/
executor.shutdown(); executor.shutdown();
try { try {
//executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // forever
executor.awaitTermination(5, TimeUnit.SECONDS); executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();

View file

@ -17,12 +17,15 @@
package io.bitsquare.arbitration; package io.bitsquare.arbitration;
import com.google.common.util.concurrent.MoreExecutors;
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;
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.FirstPeerAuthenticatedListener; import io.bitsquare.p2p.FirstPeerAuthenticatedListener;
import io.bitsquare.p2p.P2PService; import io.bitsquare.p2p.P2PService;
@ -33,7 +36,6 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableMap; import javafx.collections.ObservableMap;
import org.bitcoinj.core.ECKey; import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,11 +43,12 @@ import javax.annotation.Nullable;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.SignatureException; import java.security.SignatureException;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -87,6 +90,7 @@ public class ArbitratorManager {
private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee"; private static final String publicKeyForTesting = "027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee";
private final boolean isDevTest; private final boolean isDevTest;
private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener; private FirstPeerAuthenticatedListener firstPeerAuthenticatedListener;
private ScheduledThreadPoolExecutor republishArbitratorExecutor;
@Inject @Inject
public ArbitratorManager(@Named(ProgramArguments.DEV_TEST) boolean isDevTest, KeyRing keyRing, ArbitratorService arbitratorService, User user) { public ArbitratorManager(@Named(ProgramArguments.DEV_TEST) boolean isDevTest, KeyRing keyRing, ArbitratorService arbitratorService, User user) {
@ -108,6 +112,10 @@ public class ArbitratorManager {
}); });
} }
public void shutDown() {
MoreExecutors.shutdownAndAwaitTermination(republishArbitratorExecutor, 500, TimeUnit.MILLISECONDS);
}
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
if (user.getRegisteredArbitrator() != null) { if (user.getRegisteredArbitrator() != null) {
@ -126,10 +134,8 @@ public class ArbitratorManager {
} }
// re-publish periodically // re-publish periodically
FxTimer.runPeriodically( republishArbitratorExecutor = Utilities.getScheduledThreadPoolExecutor("", 1, 5, 5);
Duration.ofMillis(Arbitrator.TTL / 2), republishArbitratorExecutor.schedule(() -> republishArbitrator(), Arbitrator.TTL / 2, TimeUnit.MILLISECONDS);
() -> republishArbitrator()
);
} }
applyArbitrators(); applyArbitrators();
@ -183,7 +189,7 @@ public class ArbitratorManager {
resultHandler.handleResult(); resultHandler.handleResult();
if (arbitratorsObservableMap.size() > 0) if (arbitratorsObservableMap.size() > 0)
FxTimer.runLater(Duration.ofMillis(1000), this::applyArbitrators); UserThread.runAfter(() -> applyArbitrators(), 1);
}, },
errorMessageHandler::handleErrorMessage); errorMessageHandler::handleErrorMessage);
} }

View file

@ -40,7 +40,10 @@ public class FeePolicy {
// Other good source is: https://tradeblock.com/blockchain 15-100 satoshis/byte // Other good source is: https://tradeblock.com/blockchain 15-100 satoshis/byte
public static final Coin TX_FEE = Coin.valueOf(30000); // 0.0003 BTC about 0.06 EUR @ 200 EUR/BTC: about 90 satoshi /byte // On testnet currently fees are 0.002 BTC, so increase fee to 0.005 BTC to get for sure into blocks for testing
public static final Coin TX_FEE = Coin.valueOf(500000); // 0.005 BTC about 0.06 EUR @ 200 EUR/BTC: about 90 satoshi /byte
// TODO use original value again for mainnet
// public static final Coin TX_FEE = Coin.valueOf(30000); // 0.0003 BTC about 0.06 EUR @ 200 EUR/BTC: about 90 satoshi /byte
static { static {
// we use our fee as default fee // we use our fee as default fee
@ -49,11 +52,11 @@ public class FeePolicy {
public static final Coin DUST = Coin.valueOf(546); public static final Coin DUST = Coin.valueOf(546);
//TODO for testing // TODO use original value again for mainnet
public static final Coin CREATE_OFFER_FEE = Coin.valueOf(40000); // 0.0001 BTC 0.1% of 1 BTC about 0.2 EUR @ 200 EUR/BTC public static final Coin CREATE_OFFER_FEE = Coin.valueOf(40000); // 0.0001 BTC 0.1% of 1 BTC about 0.2 EUR @ 200 EUR/BTC
//public static final Coin CREATE_OFFER_FEE = Coin.valueOf(100000); // 0.001 BTC 0.1% of 1 BTC about 0.2 EUR @ 200 EUR/BTC //public static final Coin CREATE_OFFER_FEE = Coin.valueOf(100000); // 0.001 BTC 0.1% of 1 BTC about 0.2 EUR @ 200 EUR/BTC
public static final Coin TAKE_OFFER_FEE = CREATE_OFFER_FEE; public static final Coin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
//TODO for testing // TODO use original value again for mainnet
public static final Coin SECURITY_DEPOSIT = Coin.valueOf(10000); // 0.0001 BTC; about 20 EUR @ 200 EUR/BTC public static final Coin SECURITY_DEPOSIT = Coin.valueOf(10000); // 0.0001 BTC; about 20 EUR @ 200 EUR/BTC
//public static final Coin SECURITY_DEPOSIT = Coin.valueOf(10000000); // 0.1 BTC; about 20 EUR @ 200 EUR/BTC //public static final Coin SECURITY_DEPOSIT = Coin.valueOf(10000000); // 0.1 BTC; about 20 EUR @ 200 EUR/BTC
} }

View file

@ -141,7 +141,8 @@ public class TradeWalletService {
public Transaction createTradingFeeTx(AddressEntry addressEntry, Coin tradingFee, String feeReceiverAddresses) public Transaction createTradingFeeTx(AddressEntry addressEntry, Coin tradingFee, String feeReceiverAddresses)
throws InsufficientMoneyException, AddressFormatException { throws InsufficientMoneyException, AddressFormatException {
Transaction tradingFeeTx = new Transaction(params); Transaction tradingFeeTx = new Transaction(params);
Preconditions.checkArgument(tradingFee.compareTo(FeePolicy.TX_FEE) > 0); Preconditions.checkArgument(tradingFee.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0,
"You cannot send an amount which are smaller than the fee + dust output.");
Coin outPutAmount = tradingFee.subtract(FeePolicy.TX_FEE); Coin outPutAmount = tradingFee.subtract(FeePolicy.TX_FEE);
tradingFeeTx.addOutput(outPutAmount, new Address(params, feeReceiverAddresses)); tradingFeeTx.addOutput(outPutAmount, new Address(params, feeReceiverAddresses));

View file

@ -40,8 +40,6 @@ import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.utils.Threading; import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.DeterministicSeed; import org.bitcoinj.wallet.DeterministicSeed;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
@ -52,7 +50,6 @@ import javax.inject.Named;
import java.io.File; import java.io.File;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -70,7 +67,7 @@ public class WalletService {
public static final String DIR_KEY = "wallet.dir"; public static final String DIR_KEY = "wallet.dir";
public static final String PREFIX_KEY = "wallet.prefix"; public static final String PREFIX_KEY = "wallet.prefix";
private static final long STARTUP_TIMEOUT = 60 * 1000; private static final long STARTUP_TIMEOUT_SEC = 60;
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
private final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
@ -127,13 +124,10 @@ public class WalletService {
Threading.USER_THREAD = UserThread.getExecutor(); Threading.USER_THREAD = UserThread.getExecutor();
Timer timeoutTimer = FxTimer.runLater( Timer timeoutTimer = UserThread.runAfter(() -> {
Duration.ofMillis(STARTUP_TIMEOUT), Utilities.setThreadName("WalletService:StartupTimeout");
() -> { exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT_SEC / 1000 + " seconds."));
Utilities.setThreadName("WalletService:StartupTimeout"); }, STARTUP_TIMEOUT_SEC);
exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT / 1000 + " seconds."));
}
);
// If seed is non-null it means we are restoring from backup. // If seed is non-null it means we are restoring from backup.
walletAppKit = new WalletAppKit(params, walletDir, "Bitsquare") { walletAppKit = new WalletAppKit(params, walletDir, "Bitsquare") {
@ -194,8 +188,10 @@ public class WalletService {
// set after wallet is ready // set after wallet is ready
tradeWalletService.setWalletAppKit(walletAppKit); tradeWalletService.setWalletAppKit(walletAppKit);
timeoutTimer.stop(); timeoutTimer.cancel();
UserThread.execute(resultHandler::handleResult);
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
UserThread.runAfter(() -> resultHandler.handleResult(), 100, TimeUnit.MILLISECONDS);
} }
}; };
@ -240,7 +236,7 @@ public class WalletService {
public void failed(@NotNull Service.State from, @NotNull Throwable failure) { public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
walletAppKit = null; walletAppKit = null;
log.error("walletAppKit failed"); log.error("walletAppKit failed");
timeoutTimer.stop(); timeoutTimer.cancel();
UserThread.execute(() -> exceptionHandler.handleException(failure)); UserThread.execute(() -> exceptionHandler.handleException(failure));
} }
}, Threading.USER_THREAD); }, Threading.USER_THREAD);
@ -451,6 +447,8 @@ public class WalletService {
KeyParameter aesKey, KeyParameter aesKey,
FutureCallback<Transaction> callback) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException { FutureCallback<Transaction> callback) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
checkArgument(amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0,
"You cannot send an amount which are smaller than the fee + dust output.");
tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, toAddress)); tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, toAddress));
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);

View file

@ -18,17 +18,15 @@
package io.bitsquare.trade.offer; package io.bitsquare.trade.offer;
import io.bitsquare.app.Version; import io.bitsquare.app.Version;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities; 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;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.Serializable; import java.io.Serializable;
import java.time.Duration;
import java.util.Date; import java.util.Date;
public class OpenOffer implements Tradable, Serializable { public class OpenOffer implements Tradable, Serializable {
@ -38,7 +36,8 @@ public class OpenOffer implements Tradable, Serializable {
private static final Logger log = LoggerFactory.getLogger(OpenOffer.class); private static final Logger log = LoggerFactory.getLogger(OpenOffer.class);
// Timeout for offer reservation during takeoffer process. If deposit tx is not completed in that time we reset the offer to AVAILABLE state. // Timeout for offer reservation during takeoffer process. If deposit tx is not completed in that time we reset the offer to AVAILABLE state.
transient private static final long TIMEOUT = 30 * 1000; transient private static final long TIMEOUT_SEC = 30;
private java.util.Timer timeoutTimer;
public enum State { public enum State {
AVAILABLE, AVAILABLE,
@ -50,7 +49,6 @@ public class OpenOffer implements Tradable, Serializable {
private final Offer offer; private final Offer offer;
private State state = State.AVAILABLE; private State state = State.AVAILABLE;
transient private Timer timeoutTimer;
transient private Storage<TradableList<OpenOffer>> storage; transient private Storage<TradableList<OpenOffer>> storage;
public OpenOffer(Offer offer, Storage<TradableList<OpenOffer>> storage) { public OpenOffer(Offer offer, Storage<TradableList<OpenOffer>> storage) {
@ -100,19 +98,17 @@ public class OpenOffer implements Tradable, Serializable {
private void startTimeout() { private void startTimeout() {
stopTimeout(); stopTimeout();
timeoutTimer = FxTimer.runLater( timeoutTimer = UserThread.runAfter(() -> {
Duration.ofMillis(TIMEOUT), Utilities.setThreadName("OpenOffer:Timeout");
() -> { log.info("Timeout reached");
Utilities.setThreadName("OpenOffer:Timeout"); if (state == State.RESERVED)
log.info("Timeout reached"); setState(State.AVAILABLE);
if (state == State.RESERVED) }, TIMEOUT_SEC);
setState(State.AVAILABLE);
});
} }
private void stopTimeout() { private void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.stop(); timeoutTimer.cancel();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -40,17 +40,16 @@ import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Named; import javax.inject.Named;
import java.io.File; import java.io.File;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
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;
@ -189,8 +188,9 @@ public class OpenOfferManager {
offerBookService.removeOfferAtShutDown(openOffer.getOffer()); offerBookService.removeOfferAtShutDown(openOffer.getOffer());
} }
// delay a bit before we signal that we are done to give time for network
if (completeHandler != null) if (completeHandler != null)
FxTimer.runLater(Duration.ofMillis(500), completeHandler::run); UserThread.runAfter(() -> completeHandler.run(), 500, TimeUnit.MILLISECONDS);
} }
} }

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.availability; package io.bitsquare.trade.protocol.availability;
import io.bitsquare.common.UserThread;
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;
@ -29,27 +30,23 @@ import io.bitsquare.trade.protocol.availability.messages.OfferMessage;
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress; import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse; import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest; import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.Duration;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class OfferAvailabilityProtocol { public class OfferAvailabilityProtocol {
private static final Logger log = LoggerFactory.getLogger(OfferAvailabilityProtocol.class); private static final Logger log = LoggerFactory.getLogger(OfferAvailabilityProtocol.class);
private static final long TIMEOUT = 30 * 1000; private static final long TIMEOUT_SEC = 30;
private final OfferAvailabilityModel model; private final OfferAvailabilityModel model;
private final ResultHandler resultHandler; private final ResultHandler resultHandler;
private final ErrorMessageHandler errorMessageHandler; private final ErrorMessageHandler errorMessageHandler;
private final DecryptedMailListener decryptedMailListener; private final DecryptedMailListener decryptedMailListener;
private Timer timeoutTimer;
private TaskRunner<OfferAvailabilityModel> taskRunner; private TaskRunner<OfferAvailabilityModel> taskRunner;
private java.util.Timer timeoutTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -144,16 +141,16 @@ public class OfferAvailabilityProtocol {
private void startTimeout() { private void startTimeout() {
stopTimeout(); stopTimeout();
timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> { timeoutTimer = UserThread.runAfter(() -> {
Utilities.setThreadName("OfferAvailabilityProtocol:Timeout"); 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.");
}); }, TIMEOUT_SEC);
} }
private void stopTimeout() { private void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.stop(); timeoutTimer.cancel();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -17,6 +17,7 @@
package io.bitsquare.trade.protocol.trade; package io.bitsquare.trade.protocol.trade;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.crypto.PubKeyRing; import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Address; import io.bitsquare.p2p.Address;
@ -28,24 +29,21 @@ import io.bitsquare.trade.TakerTrade;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.Duration;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
public abstract class TradeProtocol { public abstract class TradeProtocol {
private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class); private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class);
private static final long TIMEOUT = 30 * 1000; private static final long TIMEOUT_SEC = 30;
protected final ProcessModel processModel; protected final ProcessModel processModel;
private final DecryptedMailListener decryptedMailListener; private final DecryptedMailListener decryptedMailListener;
private Timer timeoutTimer;
protected Trade trade; protected Trade trade;
private java.util.Timer timeoutTimer;
public TradeProtocol(Trade trade) { public TradeProtocol(Trade trade) {
this.trade = trade; this.trade = trade;
@ -125,18 +123,18 @@ public abstract class TradeProtocol {
protected void startTimeout() { protected void startTimeout() {
stopTimeout(); stopTimeout();
timeoutTimer = FxTimer.runLater(Duration.ofMillis(TIMEOUT), () -> { timeoutTimer = UserThread.runAfter(() -> {
Utilities.setThreadName("TradeProtocol:Timeout"); Utilities.setThreadName("TradeProtocol:Timeout");
log.error("Timeout reached"); log.error("Timeout reached");
trade.setErrorMessage("A timeout occurred."); trade.setErrorMessage("A timeout occurred.");
cleanupTradable(); cleanupTradable();
cleanup(); cleanup();
}); }, TIMEOUT_SEC);
} }
protected void stopTimeout() { protected void stopTimeout() {
if (timeoutTimer != null) { if (timeoutTimer != null) {
timeoutTimer.stop(); timeoutTimer.cancel();
timeoutTimer = null; timeoutTimer = null;
} }
} }

View file

@ -21,6 +21,7 @@ import ch.qos.logback.classic.Logger;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import io.bitsquare.alert.AlertManager; import io.bitsquare.alert.AlertManager;
import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ResultHandler; import io.bitsquare.common.handlers.ResultHandler;
@ -303,6 +304,8 @@ public class BitsquareApp extends Application {
log.debug("gracefulShutDown"); log.debug("gracefulShutDown");
try { try {
if (injector != null) { if (injector != null) {
ArbitratorManager arbitratorManager = injector.getInstance(ArbitratorManager.class);
arbitratorManager.shutDown();
OpenOfferManager openOfferManager = injector.getInstance(OpenOfferManager.class); OpenOfferManager openOfferManager = injector.getInstance(OpenOfferManager.class);
openOfferManager.shutDown(() -> { openOfferManager.shutDown(() -> {
P2PService p2PService = injector.getInstance(P2PService.class); P2PService p2PService = injector.getInstance(P2PService.class);

View file

@ -68,10 +68,6 @@ import java.util.stream.Stream;
class MainViewModel implements ViewModel { class MainViewModel implements ViewModel {
private static final Logger log = LoggerFactory.getLogger(MainViewModel.class); private static final Logger log = LoggerFactory.getLogger(MainViewModel.class);
private static final long BLOCKCHAIN_SYNC_TIMEOUT = 60000;
private static final long LOST_P2P_CONNECTION_TIMEOUT = 5000;
// private static final long LOST_BTC_CONNECTION_TIMEOUT = 5000;
private final WalletService walletService; private final WalletService walletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final ArbitratorManager arbitratorManager; private final ArbitratorManager arbitratorManager;
@ -111,12 +107,10 @@ class MainViewModel implements ViewModel {
private final String btcNetworkAsString; private final String btcNetworkAsString;
private Timer blockchainSyncTimeoutTimer;
private Timer lostP2PConnectionTimeoutTimer;
private MonadicBinding<Boolean> allServicesDone; private MonadicBinding<Boolean> allServicesDone;
private User user; private User user;
private int numBTCPeers = 0; private int numBTCPeers = 0;
//private Timer lostBTCConnectionTimeoutTimer; private Timer checkForBtcSyncStateTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -130,7 +124,6 @@ class MainViewModel implements ViewModel {
User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup, User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup,
BSFormatter formatter) { BSFormatter formatter) {
this.user = user; this.user = user;
log.debug("in");
this.walletService = walletService; this.walletService = walletService;
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.arbitratorManager = arbitratorManager; this.arbitratorManager = arbitratorManager;
@ -164,7 +157,7 @@ class MainViewModel implements ViewModel {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void initializeAllServices() { public void initializeAllServices() {
log.trace("initializeAllServices"); Log.traceCall();
BooleanProperty walletInitialized = initBitcoinWallet(); BooleanProperty walletInitialized = initBitcoinWallet();
BooleanProperty p2pNetWorkReady = initP2PNetwork(); BooleanProperty p2pNetWorkReady = initP2PNetwork();
@ -232,9 +225,6 @@ class MainViewModel implements ViewModel {
} }
private BooleanProperty initBitcoinWallet() { private BooleanProperty initBitcoinWallet() {
if (walletService.downloadPercentageProperty().get() > -1)
startBlockchainSyncTimeout();
EasyBind.subscribe(walletService.downloadPercentageProperty(), newValue -> setBitcoinNetworkSyncProgress((double) newValue)); EasyBind.subscribe(walletService.downloadPercentageProperty(), newValue -> setBitcoinNetworkSyncProgress((double) newValue));
walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> { walletService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
@ -258,8 +248,7 @@ class MainViewModel implements ViewModel {
} }
private void onAllServicesInitialized() { private void onAllServicesInitialized() {
log.trace("onAllServicesInitialized"); Log.traceCall();
// disputeManager // disputeManager
disputeManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) change -> { disputeManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) change -> {
@ -318,8 +307,9 @@ class MainViewModel implements ViewModel {
} }
}); });
updateBalance(); updateBalance();
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
checkPeriodicallyForBtcSyncState();
// openOfferManager // openOfferManager
openOfferManager.getOpenOffers().addListener((ListChangeListener<OpenOffer>) c -> updateBalance()); openOfferManager.getOpenOffers().addListener((ListChangeListener<OpenOffer>) c -> updateBalance());
@ -364,6 +354,19 @@ class MainViewModel implements ViewModel {
showAppScreen.set(true); showAppScreen.set(true);
} }
private void checkPeriodicallyForBtcSyncState() {
if (walletService.downloadPercentageProperty().get() == -1) {
checkForBtcSyncStateTimer = FxTimer.runPeriodically(Duration.ofSeconds(10),
() -> {
log.info("Bitcoin blockchain sync still not started.");
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
}
);
} else {
stopCheckForBtcSyncStateTimer();
}
}
private void updateP2pNetworkInfoWithPeersChanged(int numAuthenticatedPeers) { private void updateP2pNetworkInfoWithPeersChanged(int numAuthenticatedPeers) {
p2PNetworkInfo.set("Nr. of connections: " + numAuthenticatedPeers); p2PNetworkInfo.set("Nr. of connections: " + numAuthenticatedPeers);
} }
@ -566,40 +569,27 @@ class MainViewModel implements ViewModel {
btcSyncProgress.set(value); btcSyncProgress.set(value);
String numPeers = "Nr. of peers: " + numBTCPeers; String numPeers = "Nr. of peers: " + numBTCPeers;
if (value == 1) { if (value == 1) {
stopBlockchainSyncTimeout();
btcSplashInfo.set(numPeers + " / synchronized with " + btcNetworkAsString); btcSplashInfo.set(numPeers + " / synchronized with " + btcNetworkAsString);
btcFooterInfo.set(numPeers + " / synchronized with " + btcNetworkAsString); btcFooterInfo.set(numPeers + " / synchronized with " + btcNetworkAsString);
btcSplashSyncIconId.set("image-connection-synced"); btcSplashSyncIconId.set("image-connection-synced");
stopCheckForBtcSyncStateTimer();
} else if (value > 0.0) { } else if (value > 0.0) {
// We stop as soon the download started the timeout
stopBlockchainSyncTimeout();
String percentage = formatter.formatToPercent(value); String percentage = formatter.formatToPercent(value);
btcSplashInfo.set(numPeers + " / synchronizing with " + btcNetworkAsString + ": " + percentage); btcSplashInfo.set(numPeers + " / synchronizing with " + btcNetworkAsString + ": " + percentage);
btcFooterInfo.set(numPeers + " / synchronizing " + btcNetworkAsString + ": " + percentage); btcFooterInfo.set(numPeers + " / synchronizing " + btcNetworkAsString + ": " + percentage);
stopCheckForBtcSyncStateTimer();
} else if (value == -1) { } else if (value == -1) {
// not ready yet
btcSplashInfo.set(numPeers + " / connecting to " + btcNetworkAsString); btcSplashInfo.set(numPeers + " / connecting to " + btcNetworkAsString);
btcFooterInfo.set(numPeers + " / connecting to " + btcNetworkAsString); btcFooterInfo.set(numPeers + " / connecting to " + btcNetworkAsString);
} else {
log.error("Not allowed value at setBitcoinNetworkSyncProgress: " + value);
} }
} }
private void startBlockchainSyncTimeout() { private void stopCheckForBtcSyncStateTimer() {
log.trace("startBlockchainSyncTimeout"); if (checkForBtcSyncStateTimer != null) {
stopBlockchainSyncTimeout(); checkForBtcSyncStateTimer.stop();
checkForBtcSyncStateTimer = null;
blockchainSyncTimeoutTimer = FxTimer.runLater(Duration.ofMillis(BLOCKCHAIN_SYNC_TIMEOUT), () -> {
log.trace("Timeout reached");
setWalletServiceException(new TimeoutException());
});
}
private void stopBlockchainSyncTimeout() {
if (blockchainSyncTimeoutTimer != null) {
log.trace("stopBlockchainSyncTimeout");
blockchainSyncTimeoutTimer.stop();
blockchainSyncTimeoutTimer = null;
} }
} }
} }

View file

@ -20,7 +20,6 @@ package io.bitsquare.gui.main.offer.createoffer;
import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
@ -57,9 +56,10 @@ import javafx.scene.text.Font;
import javafx.stage.Window; import javafx.stage.Window;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.concurrent.TimeUnit; import java.time.Duration;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
import static javafx.beans.binding.Bindings.createStringBinding; import static javafx.beans.binding.Bindings.createStringBinding;
@ -432,19 +432,21 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
} }
if (newValue) { if (newValue) {
UserThread.runAfter(() -> { FxTimer.runLater(Duration.ofMillis(100),
new Popup().headLine(BSResources.get("createOffer.success.headline")) () -> {
.message(BSResources.get("createOffer.success.info")) new Popup().headLine(BSResources.get("createOffer.success.headline"))
.actionButtonText("Go to \"Open offers\"") .message(BSResources.get("createOffer.success.info"))
.onAction(() -> { .actionButtonText("Go to \"Open offers\"")
close(); .onAction(() -> {
UserThread.runAfter(() -> close();
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class), FxTimer.runLater(Duration.ofMillis(100),
100, TimeUnit.MILLISECONDS); () -> navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class)
}) );
.onClose(() -> close()) })
.show(); .onClose(() -> close())
}, 100, TimeUnit.MILLISECONDS); .show();
}
);
} }
}; };
} }

View file

@ -125,7 +125,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
@Override @Override
protected void activate() { protected void activate() {
if (BitsquareApp.DEV_MODE) { if (BitsquareApp.DEV_MODE) {
amount.set("0.0001"); amount.set("0.01");
minAmount.set(amount.get()); minAmount.set(amount.get());
price.set("400"); price.set("400");
volume.set("0.04"); volume.set("0.04");

View file

@ -20,7 +20,6 @@ package io.bitsquare.gui.main.offer.takeoffer;
import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
@ -60,11 +59,12 @@ import org.controlsfx.control.action.Action;
import org.controlsfx.dialog.Dialog; import org.controlsfx.dialog.Dialog;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
import static javafx.beans.binding.Bindings.createStringBinding; import static javafx.beans.binding.Bindings.createStringBinding;
@ -223,18 +223,20 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
} }
if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) { if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) {
UserThread.runAfter(() -> { FxTimer.runLater(Duration.ofMillis(100),
new Popup().information(BSResources.get("takeOffer.success.info")) () -> {
.actionButtonText("Go to \"Open trades\"") new Popup().information(BSResources.get("takeOffer.success.info"))
.onAction(() -> { .actionButtonText("Go to \"Open trades\"")
close(); .onAction(() -> {
UserThread.runAfter(() -> close();
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class), FxTimer.runLater(Duration.ofMillis(100),
100, TimeUnit.MILLISECONDS); () -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
}) );
.onClose(() -> close()) })
.show(); .onClose(() -> close())
}, 100, TimeUnit.MILLISECONDS); .show();
}
);
} }
}); });

View file

@ -44,8 +44,10 @@ import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.bitcoinj.core.Peer; import org.bitcoinj.core.Peer;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -206,7 +208,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
"Do you want to shut down now?") "Do you want to shut down now?")
.onAction(() -> { .onAction(() -> {
preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem()); preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem());
UserThread.runAfter(() -> BitsquareApp.shutDownHandler.run(), 1); FxTimer.runLater(Duration.ofMillis(500), () -> BitsquareApp.shutDownHandler.run());
}) })
.actionButtonText("Shut down") .actionButtonText("Shut down")
.closeButtonText("Cancel") .closeButtonText("Cancel")

View file

@ -32,6 +32,7 @@ import javafx.scene.layout.HBox;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
import org.reactfx.util.FxTimer; import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,6 +42,7 @@ import javax.inject.Inject;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
public class EmptyWalletPopup extends Popup { public class EmptyWalletPopup extends Popup {
@ -94,7 +96,9 @@ public class EmptyWalletPopup extends Popup {
10); 10);
Coin totalBalance = walletService.getAvailableBalance(); Coin totalBalance = walletService.getAvailableBalance();
boolean isBalanceSufficient = totalBalance.compareTo(FeePolicy.TX_FEE) >= 0; checkArgument(totalBalance.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0,
"You cannot send an amount which are smaller than the fee + dust output.");
boolean isBalanceSufficient = totalBalance.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
addressTextField = addLabelTextField(gridPane, ++rowIndex, "Your available wallet balance:", addressTextField = addLabelTextField(gridPane, ++rowIndex, "Your available wallet balance:",
formatter.formatCoinWithCode(totalBalance), 10).second; formatter.formatCoinWithCode(totalBalance), 10).second;
Tuple2<Label, InputTextField> tuple = addLabelInputTextField(gridPane, ++rowIndex, "Your destination address:"); Tuple2<Label, InputTextField> tuple = addLabelInputTextField(gridPane, ++rowIndex, "Your destination address:");

View file

@ -2,9 +2,11 @@ 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.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import io.bitsquare.app.Log; 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.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
@ -17,7 +19,11 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.*; import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -26,12 +32,11 @@ public class MaintenanceManager implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(MaintenanceManager.class); private static final Logger log = LoggerFactory.getLogger(MaintenanceManager.class);
private static final int INACTIVITY_PERIOD_BEFORE_PING = 5 * 60 * 1000; private static final int INACTIVITY_PERIOD_BEFORE_PING = 5 * 60 * 1000;
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final Supplier<Map<Address, Peer>> authenticatedPeersSupplier; private final Supplier<Map<Address, Peer>> authenticatedPeersSupplier;
private final Consumer<Address> removePeerConsumer; private final Consumer<Address> removePeerConsumer;
private final ScheduledThreadPoolExecutor executor;
private Timer sendPingTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -46,15 +51,19 @@ public class MaintenanceManager implements MessageListener {
this.removePeerConsumer = removePeerConsumer; this.removePeerConsumer = removePeerConsumer;
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
startMaintenanceTimer();
executor = Utilities.getScheduledThreadPoolExecutor("MaintenanceManager", 1, 10, 5);
executor.schedule(() -> {
UserThread.execute(() -> pingPeers());
return null;
}, 5, TimeUnit.MINUTES);
} }
public void shutDown() { public void shutDown() {
Log.traceCall(); Log.traceCall();
if (sendPingTimer != null)
sendPingTimer.cancel();
networkNode.removeMessageListener(this); networkNode.removeMessageListener(this);
MoreExecutors.shutdownAndAwaitTermination(executor, 500, TimeUnit.MILLISECONDS);
} }
@ -94,24 +103,13 @@ public class MaintenanceManager implements MessageListener {
} }
} }
private void startMaintenanceTimer() {
Log.traceCall();
if (sendPingTimer != null)
sendPingTimer.cancel();
sendPingTimer = UserThread.runAfterRandomDelay(() -> {
pingPeers();
startMaintenanceTimer();
}, 5, 7, TimeUnit.MINUTES);
}
private void pingPeers() { private void pingPeers() {
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeersSupplier.get().values()); Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeersSupplier.get().values());
if (!connectedPeersList.isEmpty()) { if (!connectedPeersList.isEmpty()) {
Log.traceCall(); Log.traceCall();
connectedPeersList.stream() connectedPeersList.stream()
.filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > INACTIVITY_PERIOD_BEFORE_PING) .filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > INACTIVITY_PERIOD_BEFORE_PING)
.forEach(e -> UserThread.runAfterRandomDelay(() -> { .forEach(e -> {
SettableFuture<Connection> future = networkNode.sendMessage(e.connection, new PingMessage(e.pingNonce)); SettableFuture<Connection> future = networkNode.sendMessage(e.connection, new PingMessage(e.pingNonce));
Futures.addCallback(future, new FutureCallback<Connection>() { Futures.addCallback(future, new FutureCallback<Connection>() {
@Override @Override
@ -125,7 +123,7 @@ public class MaintenanceManager implements MessageListener {
removePeerConsumer.accept(e.address); removePeerConsumer.accept(e.address);
} }
}); });
}, 2, 4, TimeUnit.SECONDS)); });
} }
} }
} }