From 850b6d209c602c0f1f9f75e74ee14cc8fa8ab891 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 14 Feb 2016 12:58:30 +0100 Subject: [PATCH] UI improvements, P2P network WIP --- .../btc/pricefeed/GetPriceRequest.java | 3 +- .../btc/pricefeed/MarketPriceFeed.java | 9 +- .../BitcoinAveragePriceProvider.java | 3 +- .../providers/PoloniexPriceProvider.java | 3 +- .../java/io/bitsquare/locale/CountryUtil.java | 4 + .../io/bitsquare/locale/CurrencyUtil.java | 17 +- .../io/bitsquare/locale/TradeCurrency.java | 4 + .../payment/AliPayAccountContractData.java | 5 + .../BlockChainAccountContractData.java | 7 +- .../payment/OKPayAccountContractData.java | 5 +- .../payment/PaymentAccountContractData.java | 2 + .../PerfectMoneyAccountContractData.java | 5 + .../payment/SepaAccountContractData.java | 8 + .../payment/SwishAccountContractData.java | 5 + .../main/java/io/bitsquare/trade/Trade.java | 8 +- .../java/io/bitsquare/trade/TradeManager.java | 6 +- .../io/bitsquare/trade/offer/OpenOffer.java | 15 +- .../buyer/ProcessFinalizePayoutTxRequest.java | 4 +- .../buyer/SignAndPublishDepositTxAsBuyer.java | 4 +- .../ProcessDepositTxPublishedMessage.java | 11 +- .../offerer/SendPublishDepositTxRequest.java | 4 +- .../ProcessFiatTransferStartedMessage.java | 4 +- .../ProcessPayoutTxFinalizedMessage.java | 6 +- .../SignAndPublishDepositTxAsSeller.java | 4 +- .../main/java/io/bitsquare/gui/bitsquare.css | 43 ---- .../gui/components/TextFieldWithCopyIcon.java | 4 + .../gui/components/TxIdTextField.java | 7 +- .../components/paymentmethods/AliPayForm.java | 4 +- .../paymentmethods/BlockChainForm.java | 12 +- .../paymentmethods/PaymentMethodForm.java | 6 +- .../paymentmethods/PerfectMoneyForm.java | 4 +- .../components/paymentmethods/SepaForm.java | 10 +- .../components/paymentmethods/SwishForm.java | 4 +- .../bitsquare/gui/main/debug/DebugView.java | 10 +- .../markets/charts/MarketsChartsView.java | 2 +- .../offer/createoffer/CreateOfferView.java | 6 +- .../main/offer/offerbook/OfferBookView.java | 4 +- .../main/offer/takeoffer/TakeOfferView.java | 4 +- .../settings/preferences/PreferencesView.java | 2 +- .../gui/popups/OfferDetailsPopup.java | 8 +- .../io/bitsquare/gui/util/FormBuilder.java | 7 +- .../java/io/bitsquare/http/HttpClient.java | 1 + .../java/io/bitsquare/p2p/P2PService.java | 14 +- .../io/bitsquare/p2p/network/Connection.java | 28 ++- .../p2p/peers/GetPeersRequestHandler.java | 122 ++++++++++ .../p2p/peers/MaintenanceHandshake.java | 7 +- .../p2p/peers/MaintenanceManager.java | 49 ++-- .../p2p/peers/PeerExchangeHandler.java | 158 +++++++++++++ .../p2p/peers/PeerExchangeHandshake.java | 219 ------------------ .../p2p/peers/PeerExchangeManager.java | 178 +++++++++----- .../io/bitsquare/p2p/peers/PeerManager.java | 139 ++++++----- .../p2p/peers/RequestDataManager.java | 7 +- .../p2p/seed/SeedNodesRepository.java | 8 +- .../p2p/storage/data/ProtectedData.java | 3 + 54 files changed, 684 insertions(+), 532 deletions(-) create mode 100644 network/src/main/java/io/bitsquare/p2p/peers/GetPeersRequestHandler.java create mode 100644 network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandler.java delete mode 100644 network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandshake.java diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/GetPriceRequest.java b/core/src/main/java/io/bitsquare/btc/pricefeed/GetPriceRequest.java index 5392f694b0..704426d10c 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/GetPriceRequest.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/GetPriceRequest.java @@ -1,7 +1,6 @@ package io.bitsquare.btc.pricefeed; import com.google.common.util.concurrent.*; -import io.bitsquare.app.Log; import io.bitsquare.btc.pricefeed.providers.PriceProvider; import io.bitsquare.common.util.Utilities; import org.jetbrains.annotations.NotNull; @@ -44,7 +43,7 @@ class GetPriceRequest { } private SettableFuture requestPrice(String currencyCode, PriceProvider provider, SettableFuture resultFuture) { - Log.traceCall(currencyCode); + // Log.traceCall(currencyCode); ListenableFuture future = executorService.submit(() -> { Thread.currentThread().setName("requestPrice-" + provider.toString()); return provider.getPrice(currencyCode); diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java index 31a49a21bc..a1c716e4a5 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/MarketPriceFeed.java @@ -4,7 +4,6 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import com.google.inject.Inject; -import io.bitsquare.app.Log; import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider; import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider; import io.bitsquare.btc.pricefeed.providers.PriceProvider; @@ -140,7 +139,7 @@ public class MarketPriceFeed { if (priceConsumer != null && currencyCode != null && type != null) { if (cache.containsKey(currencyCode)) { MarketPrice marketPrice = cache.get(currencyCode); - log.debug("applyPrice type=" + type); + //log.debug("applyPrice type=" + type); priceConsumer.accept(marketPrice.getPrice(type)); } else { String errorMessage = "We don't have a price for currencyCode " + currencyCode; @@ -151,14 +150,14 @@ public class MarketPriceFeed { } private void requestPrice(PriceProvider provider) { - Log.traceCall(); + //Log.traceCall(); GetPriceRequest getPriceRequest = new GetPriceRequest(); SettableFuture future = getPriceRequest.requestPrice(currencyCode, provider); Futures.addCallback(future, new FutureCallback() { public void onSuccess(MarketPrice marketPrice) { UserThread.execute(() -> { cache.put(marketPrice.currencyCode, marketPrice); - log.debug("marketPrice updated " + marketPrice); + //log.debug("marketPrice updated " + marketPrice); priceConsumer.accept(marketPrice.getPrice(type)); }); } @@ -170,7 +169,7 @@ public class MarketPriceFeed { } private void requestAllPrices(PriceProvider provider, @Nullable Runnable resultHandler) { - Log.traceCall(); + // Log.traceCall(); GetPriceRequest getPriceRequest = new GetPriceRequest(); SettableFuture> future = getPriceRequest.requestAllPrices(provider); Futures.addCallback(future, new FutureCallback>() { diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/BitcoinAveragePriceProvider.java b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/BitcoinAveragePriceProvider.java index b69d5764fd..6af764a225 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/BitcoinAveragePriceProvider.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/BitcoinAveragePriceProvider.java @@ -4,7 +4,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.internal.LinkedTreeMap; -import io.bitsquare.app.Log; import io.bitsquare.btc.pricefeed.MarketPrice; import io.bitsquare.http.HttpClient; import io.bitsquare.http.HttpException; @@ -45,7 +44,7 @@ public class BitcoinAveragePriceProvider implements PriceProvider { @Override public MarketPrice getPrice(String currencyCode) throws IOException, HttpException { - Log.traceCall("currencyCode=" + currencyCode); + //Log.traceCall("currencyCode=" + currencyCode); JsonObject jsonObject = new JsonParser() .parse(httpClient.requestWithGET(currencyCode)) .getAsJsonObject(); diff --git a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java index 4b8d0c8a63..0005a7d01c 100644 --- a/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java +++ b/core/src/main/java/io/bitsquare/btc/pricefeed/providers/PoloniexPriceProvider.java @@ -4,7 +4,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.internal.LinkedTreeMap; -import io.bitsquare.app.Log; import io.bitsquare.btc.pricefeed.MarketPrice; import io.bitsquare.http.HttpClient; import io.bitsquare.http.HttpException; @@ -63,7 +62,7 @@ public class PoloniexPriceProvider implements PriceProvider { @Override public MarketPrice getPrice(String currencyCode) throws IOException, HttpException { - Log.traceCall("currencyCode=" + currencyCode); + // Log.traceCall("currencyCode=" + currencyCode); JsonObject jsonObject = new JsonParser() .parse(httpClient.requestWithGET(currencyCode)) .getAsJsonObject(); diff --git a/core/src/main/java/io/bitsquare/locale/CountryUtil.java b/core/src/main/java/io/bitsquare/locale/CountryUtil.java index 8b4288a922..1c432b767a 100644 --- a/core/src/main/java/io/bitsquare/locale/CountryUtil.java +++ b/core/src/main/java/io/bitsquare/locale/CountryUtil.java @@ -80,6 +80,10 @@ public class CountryUtil { return new Locale(LanguageUtil.getDefaultLanguage(), countryCode).getDisplayCountry(); } + public static String getNameAndCode(String countryCode) { + return getNameByCode(countryCode) + " (" + countryCode + ")"; + } + public static String getCodesString(List countryCodes) { return countryCodes.stream().collect(Collectors.joining(", ")); } diff --git a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java index 244925670d..2551a4ed2d 100644 --- a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java +++ b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java @@ -171,12 +171,6 @@ public class CurrencyUtil { result.add(new CryptoCurrency("ETH", "Ethereum")); result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("NMC", "Namecoin")); - // Unfortunately we cannot support CryptoNote coins yet as there is no way to proof the transaction. Payment ID helps only locate the tx but the - // arbitrator cannot see if the receiving key matches the receivers address. They might add support for exposing the tx key, but that is not - // implemented yet. To use the view key (also not available in GUI wallets) would reveal the complete wallet history for incoming payments, which is - // not acceptable from privacy point of view. - // result.add(new CryptoCurrency("XMR", "Monero")); - // result.add(new CryptoCurrency("BCN", "Bytecoin")); result.add(new CryptoCurrency("DASH", "Dash")); result.add(new CryptoCurrency("NBT", "NuBits")); result.add(new CryptoCurrency("NSR", "NuShares")); @@ -192,6 +186,13 @@ public class CurrencyUtil { result.add(new CryptoCurrency("BTS", "BitShares")); result.add(new CryptoCurrency("XCP", "Counterparty")); result.add(new CryptoCurrency("XRP", "Ripple")); + + // Unfortunately we cannot support CryptoNote coins yet as there is no way to proof the transaction. Payment ID helps only locate the tx but the + // arbitrator cannot see if the receiving key matches the receivers address. They might add support for exposing the tx key, but that is not + // implemented yet. To use the view key (also not available in GUI wallets) would reveal the complete wallet history for incoming payments, which is + // not acceptable from privacy point of view. + // result.add(new CryptoCurrency("XMR", "Monero")); + // result.add(new CryptoCurrency("BCN", "Bytecoin")); return result; } @@ -213,8 +214,8 @@ public class CurrencyUtil { try { return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale()); } catch (Throwable t) { - // Seems that it is a crypto currency - return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getCodeAndName(); + // Seems that it is a cryptocurrency + return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getName(); } } diff --git a/core/src/main/java/io/bitsquare/locale/TradeCurrency.java b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java index 4bd0c07df9..5f20eb467f 100644 --- a/core/src/main/java/io/bitsquare/locale/TradeCurrency.java +++ b/core/src/main/java/io/bitsquare/locale/TradeCurrency.java @@ -58,6 +58,10 @@ public class TradeCurrency implements Serializable { return symbol; } + public String getNameAndCode() { + return name + " (" + code + ")"; + } + public String getCodeAndName() { return code + " (" + name + ")"; } diff --git a/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java b/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java index 1275d92f03..00c9b03732 100644 --- a/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/AliPayAccountContractData.java @@ -44,6 +44,11 @@ public class AliPayAccountContractData extends PaymentAccountContractData implem return "AliPay - Account nr.: " + accountNr; } + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + @Override public String toString() { return "AliPayAccountContractData{" + diff --git a/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java b/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java index d28db61a51..d611ffd6b0 100644 --- a/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/BlockChainAccountContractData.java @@ -43,9 +43,14 @@ public class BlockChainAccountContractData extends PaymentAccountContractData im @Override public String getPaymentDetails() { - return "Address: " + address; + return "Receivers cryptocurrency address: " + address; } + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + public void setPaymentId(String paymentId) { this.paymentId = paymentId; } diff --git a/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java b/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java index 7cabdd44de..6956c9b37b 100644 --- a/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/OKPayAccountContractData.java @@ -44,5 +44,8 @@ public class OKPayAccountContractData extends PaymentAccountContractData impleme return "OKPay - Account nr.: " + accountNr; } - + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } } diff --git a/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java b/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java index 0cfeb948c3..2adc243375 100644 --- a/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/PaymentAccountContractData.java @@ -72,6 +72,8 @@ public abstract class PaymentAccountContractData implements Serializable { abstract public String getPaymentDetails(); + abstract public String getPaymentDetailsForTradePopup(); + public int getMaxTradePeriod() { return maxTradePeriod; } diff --git a/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java index f710b46e39..f54dcf2f56 100644 --- a/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/PerfectMoneyAccountContractData.java @@ -44,4 +44,9 @@ public class PerfectMoneyAccountContractData extends PaymentAccountContractData return "PerfectMoney - Account nr.: " + accountNr; } + @Override + public String getPaymentDetailsForTradePopup() { + return getPaymentDetails(); + } + } diff --git a/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java b/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java index 71b64fab7d..eaa91f929f 100644 --- a/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/SepaAccountContractData.java @@ -89,4 +89,12 @@ public class SepaAccountContractData extends PaymentAccountContractData implemen public String getPaymentDetails() { return "SEPA - Holder name: " + holderName + ", IBAN: " + iban + ", BIC: " + bic + ", country code: " + getCountryCode(); } + + @Override + public String getPaymentDetailsForTradePopup() { + return "Holder name: " + holderName + "\n" + + "IBAN: " + iban + "\n" + + "BIC: " + bic + "\n" + + "Country of bank: " + CountryUtil.getNameAndCode(getCountryCode()); + } } diff --git a/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java b/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java index 1bf11ee4cb..57f7c96343 100644 --- a/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java +++ b/core/src/main/java/io/bitsquare/payment/SwishAccountContractData.java @@ -53,4 +53,9 @@ public class SwishAccountContractData extends PaymentAccountContractData impleme return "Swish - Holder name: " + holderName + ", mobile nr.: " + mobileNr; } + @Override + public String getPaymentDetailsForTradePopup() { + return "Holder name: " + holderName + "\n" + + "Mobile nr.: " + mobileNr; + } } diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index 4ae4ffb737..bcefe391c1 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -146,7 +146,7 @@ abstract public class Trade implements Tradable, Model, Serializable { // Mutable private DecryptedMsgWithPubKey decryptedMsgWithPubKey; - private Date takeOfferDate = new Date(0); // in some error cases the date is not set and cause null pointers, so we set a default + private Date takeOfferDate; private int takeOfferDateAsBlockHeight; private Coin tradeAmount; private NodeAddress tradingPeerNodeAddress; @@ -180,6 +180,7 @@ abstract public class Trade implements Tradable, Model, Serializable { protected Trade(Offer offer, Storage storage) { this.offer = offer; this.storage = storage; + this.takeOfferDate = new Date(); processModel = new ProcessModel(); tradeVolumeProperty = new SimpleObjectProperty<>(); @@ -199,6 +200,7 @@ abstract public class Trade implements Tradable, Model, Serializable { this.tradingPeerNodeAddress = tradingPeerNodeAddress; tradeAmountProperty.set(tradeAmount); tradeVolumeProperty.set(getTradeVolume()); + this.takeOfferDate = new Date(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -411,9 +413,9 @@ abstract public class Trade implements Tradable, Model, Serializable { return takeOfferDate; } - public void setTakeOfferDate(Date takeOfferDate) { + /*public void setTakeOfferDate(Date takeOfferDate) { this.takeOfferDate = takeOfferDate; - } + }*/ public int getTakeOfferDateAsBlockHeight() { return takeOfferDateAsBlockHeight; diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index af3e0c05ad..3d61a8ba3e 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -59,7 +59,6 @@ import org.spongycastle.crypto.params.KeyParameter; import javax.inject.Inject; import javax.inject.Named; import java.io.File; -import java.util.Date; import java.util.Optional; import static io.bitsquare.util.Validator.nonEmptyStringOf; @@ -98,7 +97,6 @@ public class TradeManager { ArbitratorManager arbitratorManager, P2PService p2PService, @Named("storage.dir") File storageDir) { - Log.traceCall(); this.user = user; this.keyRing = keyRing; this.walletService = walletService; @@ -280,7 +278,7 @@ public class TradeManager { else trade = new BuyerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage); - trade.setTakeOfferDate(new Date()); + //trade.setTakeOfferDate(new Date()); trade.setTakeOfferDateAsBlockHeight(tradeWalletService.getBestChainHeight()); trade.setTakerPaymentAccountId(paymentAccountId); @@ -312,8 +310,8 @@ public class TradeManager { public void onSuccess(@javax.annotation.Nullable Transaction transaction) { if (transaction != null) { log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); - trade.setState(Trade.State.WITHDRAW_COMPLETED); addTradeToClosedTrades(trade); + trade.setState(Trade.State.WITHDRAW_COMPLETED); resultHandler.handleResult(); } } diff --git a/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java b/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java index 9a85e2c8d2..3891df8bd1 100644 --- a/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java +++ b/core/src/main/java/io/bitsquare/trade/offer/OpenOffer.java @@ -25,6 +25,7 @@ import io.bitsquare.trade.TradableList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.io.Serializable; import java.util.Date; @@ -55,6 +56,18 @@ public class OpenOffer implements Tradable, Serializable { this.storage = storage; } + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + try { + in.defaultReadObject(); + + // If we have a reserved state from the local db we reset it + if (state == State.RESERVED) + setState(State.AVAILABLE); + + } catch (Throwable t) { + log.error("Cannot be deserialized." + t.getMessage()); + } + } public Date getDate() { return offer.getDate(); } @@ -98,7 +111,7 @@ public class OpenOffer implements Tradable, Serializable { stopTimeout(); timeoutTimer = UserThread.runAfter(() -> { - log.info("Timeout reached"); + log.info("Timeout for resettin State.RESERVED reached"); if (state == State.RESERVED) setState(State.AVAILABLE); }, TIMEOUT_SEC); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessFinalizePayoutTxRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessFinalizePayoutTxRequest.java index e1ffbdb3b8..8e46d42500 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessFinalizePayoutTxRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessFinalizePayoutTxRequest.java @@ -47,13 +47,13 @@ public class ProcessFinalizePayoutTxRequest extends TradeTask { processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddress)); trade.setLockTimeAsBlockHeight(nonNegativeLongOf(message.lockTimeAsBlockHeight)); - trade.setState(Trade.State.FIAT_PAYMENT_RECEIPT_MSG_RECEIVED); - // update to the latest peer address of our peer if the message is correct trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); removeMailboxMessageAfterProcessing(); + trade.setState(Trade.State.FIAT_PAYMENT_RECEIPT_MSG_RECEIVED); + complete(); } catch (Throwable t) { failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java index d62e445d33..dbf5ac52c7 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/SignAndPublishDepositTxAsBuyer.java @@ -27,8 +27,6 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - public class SignAndPublishDepositTxAsBuyer extends TradeTask { private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsBuyer.class); @@ -64,7 +62,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask { log.trace("takerSignAndPublishTx succeeded " + transaction); trade.setDepositTx(transaction); - trade.setTakeOfferDate(new Date()); + //trade.setTakeOfferDate(new Date()); trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); trade.setState(Trade.State.DEPOSIT_PUBLISHED); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java index efa8e0cbd9..047e168672 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/ProcessDepositTxPublishedMessage.java @@ -26,8 +26,6 @@ import org.bitcoinj.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static io.bitsquare.util.Validator.checkTradeId; @@ -53,11 +51,10 @@ public class ProcessDepositTxPublishedMessage extends TradeTask { Transaction transactionFromSerializedTx = processModel.getWalletService().getTransactionFromSerializedTx(message.depositTx); // update with full tx trade.setDepositTx(processModel.getTradeWalletService().addTransactionToWallet(transactionFromSerializedTx)); - - trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED); - trade.setTakeOfferDate(new Date()); - trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); + //trade.setTakeOfferDate(new Date()); + trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); + if (trade instanceof OffererTrade) processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer()); @@ -65,6 +62,8 @@ public class ProcessDepositTxPublishedMessage extends TradeTask { trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); removeMailboxMessageAfterProcessing(); + + trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java index 0b6d88ea7a..9ca9f98e06 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SendPublishDepositTxRequest.java @@ -36,7 +36,6 @@ public class SendPublishDepositTxRequest extends TradeTask { protected void run() { try { runInterceptHook(); - trade.setState(Trade.State.DEPOSIT_PUBLISH_REQUESTED); PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest( processModel.getId(), processModel.getPaymentAccountContractData(trade), @@ -69,6 +68,9 @@ public class SendPublishDepositTxRequest extends TradeTask { } } ); + + //TODO should it be in success handler? + trade.setState(Trade.State.DEPOSIT_PUBLISH_REQUESTED); } catch (Throwable t) { failed(t); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessFiatTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessFiatTransferStartedMessage.java index ac9cd6bf7b..cd4aa8f47c 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessFiatTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessFiatTransferStartedMessage.java @@ -46,13 +46,13 @@ public class ProcessFiatTransferStartedMessage extends TradeTask { processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.buyerPayoutAddress)); - trade.setState(Trade.State.FIAT_PAYMENT_STARTED_MSG_RECEIVED); - // update to the latest peer address of our peer if the message is correct trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); removeMailboxMessageAfterProcessing(); + trade.setState(Trade.State.FIAT_PAYMENT_STARTED_MSG_RECEIVED); + complete(); } catch (Throwable t) { failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessPayoutTxFinalizedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessPayoutTxFinalizedMessage.java index 791307942f..ca401bdcff 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessPayoutTxFinalizedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/ProcessPayoutTxFinalizedMessage.java @@ -46,13 +46,13 @@ public class ProcessPayoutTxFinalizedMessage extends TradeTask { checkArgument(message.payoutTx != null); trade.setPayoutTx(processModel.getWalletService().getTransactionFromSerializedTx(message.payoutTx)); - trade.setState(Trade.State.PAYOUT_TX_RECEIVED); - // update to the latest peer address of our peer if the message is correct trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); removeMailboxMessageAfterProcessing(); - + + trade.setState(Trade.State.PAYOUT_TX_RECEIVED); + complete(); } catch (Throwable t) { failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java index 1a7413b96f..aa64876945 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishDepositTxAsSeller.java @@ -27,8 +27,6 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - public class SignAndPublishDepositTxAsSeller extends TradeTask { private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsSeller.class); @@ -62,7 +60,7 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask { log.trace("takerSignAndPublishTx succeeded " + transaction); trade.setDepositTx(transaction); - trade.setTakeOfferDate(new Date()); + //trade.setTakeOfferDate(new Date()); trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); trade.setState(Trade.State.DEPOSIT_PUBLISHED); diff --git a/gui/src/main/java/io/bitsquare/gui/bitsquare.css b/gui/src/main/java/io/bitsquare/gui/bitsquare.css index c6fca62f6c..49ec553b51 100644 --- a/gui/src/main/java/io/bitsquare/gui/bitsquare.css +++ b/gui/src/main/java/io/bitsquare/gui/bitsquare.css @@ -634,49 +634,6 @@ textfield */ -fx-base: #dd0000; } -#trade-notification-warning { - -fx-font-size: 14; - -fx-base: -bs-red-soft; -} - -#trade-notification-information { - -fx-font-size: 14; - -fx-outer-border: linear-gradient(to bottom, #ffb34b, #ff9200); -} - -#trade-notification-dispute { - -fx-font-size: 14; - -fx-base: -bs-error-red; -} - -#trade-notification-support { - -fx-font-size: 14; - -fx-base: -bs-orange; -} - -#support-info-label { - -fx-font-size: 14; - -fx-text-fill: -bs-red-soft; -} - -#titled-group-bg-warn { - -fx-body-color: linear-gradient(to bottom, -bs-content-bg-grey, #F0F0F0); - -fx-outer-border: linear-gradient(to bottom, #ffb34b, #ff9200); - -fx-background-color: -fx-shadow-highlight-color, - -fx-outer-border, - -fx-inner-border, - -fx-body-color; - -fx-background-insets: 0 0 -1 0, 0, 1, 2; - -fx-background-radius: 3px, 3px, 2px, 1px; -} - -#titled-group-bg-label-warn { - -fx-font-weight: bold; - -fx-font-size: 14; - -fx-text-fill: #ff9200; - -fx-background-color: -bs-content-bg-grey; -} - /* TitledGroupBg */ #titled-group-bg-label { -fx-font-weight: bold; diff --git a/gui/src/main/java/io/bitsquare/gui/components/TextFieldWithCopyIcon.java b/gui/src/main/java/io/bitsquare/gui/components/TextFieldWithCopyIcon.java index f1ec9f0da7..a16fe9c149 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/TextFieldWithCopyIcon.java +++ b/gui/src/main/java/io/bitsquare/gui/components/TextFieldWithCopyIcon.java @@ -93,6 +93,10 @@ public class TextFieldWithCopyIcon extends AnchorPane { this.text.set(text); } + public void setTooltip(Tooltip toolTip) { + textField.setTooltip(toolTip); + } + public void setCopyWithoutCurrencyPostFix(boolean copyWithoutCurrencyPostFix) { this.copyWithoutCurrencyPostFix = copyWithoutCurrencyPostFix; } diff --git a/gui/src/main/java/io/bitsquare/gui/components/TxIdTextField.java b/gui/src/main/java/io/bitsquare/gui/components/TxIdTextField.java index 6fa5053f08..daa178d6e1 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/TxIdTextField.java +++ b/gui/src/main/java/io/bitsquare/gui/components/TxIdTextField.java @@ -75,12 +75,15 @@ public class TxIdTextField extends AnchorPane { copyIcon = new Label(); copyIcon.setLayoutY(3); copyIcon.getStyleClass().add("copy-icon"); - Tooltip.install(copyIcon, new Tooltip("Copy transaction ID to clipboard")); + copyIcon.setTooltip(new Tooltip("Copy transaction ID to clipboard")); AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); AnchorPane.setRightAnchor(copyIcon, 30.0); + Tooltip tooltip = new Tooltip("Open a blockchain explorer with that transactions ID"); + blockExplorerIcon = new Label(); blockExplorerIcon.getStyleClass().add("external-link-icon"); + blockExplorerIcon.setTooltip(tooltip); AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK); blockExplorerIcon.setMinWidth(20); AnchorPane.setRightAnchor(blockExplorerIcon, 52.0); @@ -89,7 +92,7 @@ public class TxIdTextField extends AnchorPane { textField = new TextField(); textField.setId("address-text-field"); textField.setEditable(false); - Tooltip.install(textField, new Tooltip("Open a blockchain explorer with that transactions ID")); + textField.setTooltip(tooltip); AnchorPane.setRightAnchor(textField, 80.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java index 49726e1a0a..56c822f634 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java @@ -63,7 +63,7 @@ public class AliPayForm extends PaymentMethodForm { updateFromInputs(); }); - addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); addAccountNameTextFieldWithAutoFillCheckBox(); } @@ -85,7 +85,7 @@ public class AliPayForm extends PaymentMethodForm { addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(aliPayAccount.getPaymentMethod().getId())); TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", aliPayAccount.getAccountNr()).second; field.setMouseTransparent(false); - addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); } diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java index 5738e00bc1..c54b095c73 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BlockChainForm.java @@ -49,7 +49,7 @@ public class BlockChainForm extends PaymentMethodForm { private ComboBox currencyComboBox; public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Address:", ((BlockChainAccountContractData) paymentAccountContractData).getAddress()); + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Cryptocurrency address:", ((BlockChainAccountContractData) paymentAccountContractData).getAddress()); if (paymentAccountContractData instanceof BlockChainAccountContractData && ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId() != null) addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Payment ID:", ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId()); @@ -70,7 +70,7 @@ public class BlockChainForm extends PaymentMethodForm { addTradeCurrencyComboBox(); currencyComboBox.setPrefWidth(250); - addressInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Receiving altcoin address:").second; + addressInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Cryptocurrency address:").second; addressInputTextField.setValidator(altCoinAddressValidator); addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> { @@ -98,9 +98,9 @@ public class BlockChainForm extends PaymentMethodForm { gridRowFrom = gridRow; addLabelTextField(gridPane, gridRow, "Account name:", blockChainAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(blockChainAccount.getPaymentMethod().getId())); - TextField field = addLabelTextField(gridPane, ++gridRow, "Receiving altcoin address:", blockChainAccount.getAddress()).second; + TextField field = addLabelTextField(gridPane, ++gridRow, "Cryptocurrency address:", blockChainAccount.getAddress()).second; field.setMouseTransparent(false); - addLabelTextField(gridPane, ++gridRow, "Crypto currency:", blockChainAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Crypto currency:", blockChainAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); } @@ -114,13 +114,13 @@ public class BlockChainForm extends PaymentMethodForm { @Override protected void addTradeCurrencyComboBox() { currencyComboBox = addLabelComboBox(gridPane, ++gridRow, "Crypto currency:").second; - currencyComboBox.setPromptText("Select crypto currency"); + currencyComboBox.setPromptText("Select cryptocurrency"); currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getSortedCryptoCurrencies())); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 20)); currencyComboBox.setConverter(new StringConverter() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java index bc99ee858c..f2404f837e 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PaymentMethodForm.java @@ -65,7 +65,7 @@ public abstract class PaymentMethodForm { currencyComboBox.setConverter(new StringConverter() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override @@ -111,7 +111,7 @@ public abstract class PaymentMethodForm { displayText = hours / 24 + " days"; - addLabelTextField(gridPane, gridRow, "Trade period/end date:", displayText + " / " + dateFromBlocks); + addLabelTextField(gridPane, gridRow, "Max. allowed trade period / date:", displayText + " / " + dateFromBlocks); } protected void addAllowedPeriod() { @@ -126,7 +126,7 @@ public abstract class PaymentMethodForm { displayText += " (Max. permitted period until the trade has to be completed)"; - addLabelTextField(gridPane, ++gridRow, "Allowed trade period:", displayText); + addLabelTextField(gridPane, ++gridRow, "Max. allowed trade period:", displayText); } abstract protected void autoFillNameTextField(); diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PerfectMoneyForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PerfectMoneyForm.java index 5dcefa14fd..a513f08d3a 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PerfectMoneyForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/PerfectMoneyForm.java @@ -64,7 +64,7 @@ public class PerfectMoneyForm extends PaymentMethodForm { updateFromInputs(); }); - addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); addAccountNameTextFieldWithAutoFillCheckBox(); } @@ -87,7 +87,7 @@ public class PerfectMoneyForm extends PaymentMethodForm { addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(perfectMoneyAccount.getPaymentMethod().getId())); TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", perfectMoneyAccount.getAccountNr()).second; field.setMouseTransparent(false); - addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); } diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SepaForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SepaForm.java index 7ac38077f2..58346d5530 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SepaForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SepaForm.java @@ -59,7 +59,7 @@ public class SepaForm extends PaymentMethodForm { public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account holder name:", ((SepaAccountContractData) paymentAccountContractData).getHolderName()); - addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Country of bank:", CountryUtil.getNameByCode(paymentAccountContractData.getCountryCode())); + addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Country of bank:", CountryUtil.getNameAndCode(paymentAccountContractData.getCountryCode())); addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "IBAN:", ((SepaAccountContractData) paymentAccountContractData).getIban()); addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "BIC/SWIFT:", ((SepaAccountContractData) paymentAccountContractData).getBic()); return gridRow; @@ -107,7 +107,7 @@ public class SepaForm extends PaymentMethodForm { countryComboBox.setConverter(new StringConverter() { @Override public String toString(Country country) { - return country.code + " (" + country.name + ")"; + return country.name + " (" + country.code + ")"; } @Override @@ -120,7 +120,7 @@ public class SepaForm extends PaymentMethodForm { sepaAccount.setCountry(selectedItem); TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(selectedItem.code); sepaAccount.setSingleTradeCurrency(currency); - currencyTextField.setText("Currency: " + currency.getCodeAndName()); + currencyTextField.setText("Currency: " + currency.getNameAndCode()); updateCountriesSelection(true, euroCountryCheckBoxes); updateCountriesSelection(true, nonEuroCountryCheckBoxes); updateFromInputs(); @@ -138,7 +138,7 @@ public class SepaForm extends PaymentMethodForm { sepaAccount.setCountry(country); TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(country.code); sepaAccount.setSingleTradeCurrency(currency); - currencyTextField.setText("Currency: " + currency.getCodeAndName()); + currencyTextField.setText("Currency: " + currency.getNameAndCode()); } updateFromInputs(); @@ -256,7 +256,7 @@ public class SepaForm extends PaymentMethodForm { TextField bicField = addLabelTextField(gridPane, ++gridRow, "BIC/SWIFT:", sepaAccount.getBic()).second; bicField.setMouseTransparent(false); addLabelTextField(gridPane, ++gridRow, "Location of Bank:", sepaAccount.getCountry().name); - addLabelTextField(gridPane, ++gridRow, "Currency:", sepaAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", sepaAccount.getSingleTradeCurrency().getNameAndCode()); String countries; Tooltip tooltip = null; if (CountryUtil.containsAllSepaEuroCountries(sepaAccount.getAcceptedCountryCodes())) { diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SwishForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SwishForm.java index affd00d380..46ede8940e 100644 --- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SwishForm.java +++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/SwishForm.java @@ -72,7 +72,7 @@ public class SwishForm extends PaymentMethodForm { updateFromInputs(); }); - addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); addAccountNameTextFieldWithAutoFillCheckBox(); } @@ -95,7 +95,7 @@ public class SwishForm extends PaymentMethodForm { addLabelTextField(gridPane, ++gridRow, "Account holder name:", swishAccount.getHolderName()); TextField field = addLabelTextField(gridPane, ++gridRow, "Mobile nr.:", swishAccount.getMobileNr()).second; field.setMouseTransparent(false); - addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getCodeAndName()); + addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getNameAndCode()); addAllowedPeriod(); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java b/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java index 7227f9e59c..9a406d9dbb 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/debug/DebugView.java @@ -35,9 +35,9 @@ import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.tasks.buyer.*; import io.bitsquare.trade.protocol.trade.tasks.offerer.*; import io.bitsquare.trade.protocol.trade.tasks.seller.*; +import io.bitsquare.trade.protocol.trade.tasks.shared.BroadcastAfterLockTime; import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute; -import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.taker.*; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -99,7 +99,7 @@ public class DebugView extends InitializableView { SignAndFinalizePayoutTx.class, CommitPayoutTx.class, SendPayoutTxFinalizedMessage.class, - SetupPayoutTxLockTimeReachedListener.class, + BroadcastAfterLockTime.class, Boolean.class, /* used as seperator*/ @@ -125,7 +125,7 @@ public class DebugView extends InitializableView { ProcessPayoutTxFinalizedMessage.class, CommitPayoutTx.class, - SetupPayoutTxLockTimeReachedListener.class, + BroadcastAfterLockTime.class, Boolean.class /* used as seperator*/ ) ); @@ -152,7 +152,7 @@ public class DebugView extends InitializableView { SignAndFinalizePayoutTx.class, CommitPayoutTx.class, SendPayoutTxFinalizedMessage.class, - SetupPayoutTxLockTimeReachedListener.class, + BroadcastAfterLockTime.class, Boolean.class, /* used as seperator*/ @@ -177,7 +177,7 @@ public class DebugView extends InitializableView { ProcessPayoutTxFinalizedMessage.class, CommitPayoutTx.class, - SetupPayoutTxLockTimeReachedListener.class, + BroadcastAfterLockTime.class, Boolean.class /* used as seperator*/ ) ); diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java index fe29429d59..9adeb1f471 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/markets/charts/MarketsChartsView.java @@ -86,7 +86,7 @@ public class MarketsChartsView extends ActivatableViewAndModel() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java index dcc9728297..4aed52f666 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java @@ -289,7 +289,7 @@ public class CreateOfferView extends ActivatableViewAndModel() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override @@ -673,7 +673,7 @@ public class CreateOfferView extends ActivatableViewAndModel placeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, + Tuple3 placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, BSResources.get("createOffer.fundsBox.placeOffer")); placeOfferButton = placeOfferTuple.first; placeOfferButton.setVisible(false); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java index 32a989ad0f..839c755f56 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/offerbook/OfferBookView.java @@ -89,7 +89,7 @@ public class OfferBookView extends ActivatableViewAndModel() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override @@ -160,6 +160,8 @@ public class OfferBookView extends ActivatableViewAndModel model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem())); paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); createOfferButton.setOnAction(e -> onCreateOffer()); + priceColumn.textProperty().bind(createStringBinding( + () -> "Price in " + model.tradeCurrencyCode.get() + "/BTC", model.tradeCurrencyCode)); volumeColumn.textProperty().bind(createStringBinding( () -> "Amount in " + model.tradeCurrencyCode.get() + " (Min.)", model.tradeCurrencyCode)); model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java index b393a3ea31..bbc130ed00 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java @@ -317,7 +317,7 @@ public class TakeOfferView extends ActivatableViewAndModel takeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.takeOffer")); + Tuple3 takeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.takeOffer")); takeOfferButton = takeOfferTuple.first; takeOfferButton.setVisible(false); takeOfferButton.setOnAction(e -> onTakeOffer()); diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java index 6238f3c009..16cd0d9d4d 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesView.java @@ -87,7 +87,7 @@ public class PreferencesView extends ActivatableViewAndModel() { @Override public String toString(TradeCurrency tradeCurrency) { - return tradeCurrency.getCodeAndName(); + return tradeCurrency.getNameAndCode(); } @Override diff --git a/gui/src/main/java/io/bitsquare/gui/popups/OfferDetailsPopup.java b/gui/src/main/java/io/bitsquare/gui/popups/OfferDetailsPopup.java index 0e563831c6..d6c09041f1 100644 --- a/gui/src/main/java/io/bitsquare/gui/popups/OfferDetailsPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/popups/OfferDetailsPopup.java @@ -148,7 +148,8 @@ public class OfferDetailsPopup extends Popup { addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId())); rows = 3; - if (offer.getPaymentMethodCountryCode() != null) + String paymentMethodCountryCode = offer.getPaymentMethodCountryCode(); + if (paymentMethodCountryCode != null) rows++; if (offer.getOfferFeePaymentTxID() != null) rows++; @@ -161,8 +162,9 @@ public class OfferDetailsPopup extends Popup { addLabelTextField(gridPane, rowIndex, "Offer ID:", offer.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, ++rowIndex, "Creation date:", formatter.formatDateTime(offer.getDate())); - if (offer.getPaymentMethodCountryCode() != null) - addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:", offer.getPaymentMethodCountryCode()); + if (paymentMethodCountryCode != null) + addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:", + CountryUtil.getNameAndCode(paymentMethodCountryCode)); if (offer.getAcceptedCountryCodes() != null) { String countries; Tooltip tooltip = null; diff --git a/gui/src/main/java/io/bitsquare/gui/util/FormBuilder.java b/gui/src/main/java/io/bitsquare/gui/util/FormBuilder.java index b518e1bf29..92a7896398 100644 --- a/gui/src/main/java/io/bitsquare/gui/util/FormBuilder.java +++ b/gui/src/main/java/io/bitsquare/gui/util/FormBuilder.java @@ -129,6 +129,7 @@ public class FormBuilder { return label; } + /////////////////////////////////////////////////////////////////////////////////////////// // Label + TextField /////////////////////////////////////////////////////////////////////////////////////////// @@ -672,9 +673,9 @@ public class FormBuilder { // Button + ProgressIndicator + Label /////////////////////////////////////////////////////////////////////////////////////////// - public static Tuple3 addButtonWithStatus(GridPane gridPane, - int rowIndex, - String buttonTitle) { + public static Tuple3 addButtonWithStatusAfterGroup(GridPane gridPane, + int rowIndex, + String buttonTitle) { return addButtonWithStatus(gridPane, rowIndex, buttonTitle, 15); } diff --git a/network/src/main/java/io/bitsquare/http/HttpClient.java b/network/src/main/java/io/bitsquare/http/HttpClient.java index 2edf917145..37fc6e88c5 100644 --- a/network/src/main/java/io/bitsquare/http/HttpClient.java +++ b/network/src/main/java/io/bitsquare/http/HttpClient.java @@ -20,6 +20,7 @@ public class HttpClient implements Serializable { connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(10000); + connection.setReadTimeout(10000); if (connection.getResponseCode() == 200) { return convertInputStreamToString(connection.getInputStream()); diff --git a/network/src/main/java/io/bitsquare/p2p/P2PService.java b/network/src/main/java/io/bitsquare/p2p/P2PService.java index ce232963b1..1814fe6284 100644 --- a/network/src/main/java/io/bitsquare/p2p/P2PService.java +++ b/network/src/main/java/io/bitsquare/p2p/P2PService.java @@ -16,10 +16,7 @@ import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.PrefixedSealedAndSignedMessage; import io.bitsquare.p2p.messaging.*; import io.bitsquare.p2p.network.*; -import io.bitsquare.p2p.peers.Broadcaster; -import io.bitsquare.p2p.peers.PeerExchangeManager; -import io.bitsquare.p2p.peers.PeerManager; -import io.bitsquare.p2p.peers.RequestDataManager; +import io.bitsquare.p2p.peers.*; import io.bitsquare.p2p.seed.SeedNodesRepository; import io.bitsquare.p2p.storage.HashMapChangedListener; import io.bitsquare.p2p.storage.P2PDataStorage; @@ -81,6 +78,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis private Subscription networkReadySubscription; private boolean isBootstrapped; private ChangeListener numOfBroadcastsChangeListener; + private MaintenanceManager maintenanceManager; /////////////////////////////////////////////////////////////////////////////////////////// @@ -128,6 +126,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis peerExchangeManager = new PeerExchangeManager(networkNode, peerManager, seedNodeAddresses); + maintenanceManager = new MaintenanceManager(networkNode, peerManager, seedNodeAddresses); + + // We need to have both the initial data delivered and the hidden service published networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived, (hiddenServicePublished, preliminaryDataReceived) @@ -170,6 +171,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis if (peerExchangeManager != null) peerExchangeManager.shutDown(); + if (maintenanceManager != null) + maintenanceManager.shutDown(); + if (networkNode != null) networkNode.shutDown(() -> { shutDownResultHandlers.stream().forEach(Runnable::run); @@ -222,6 +226,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis hiddenServicePublished.set(true); p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished); + + maintenanceManager.start(); } @Override diff --git a/network/src/main/java/io/bitsquare/p2p/network/Connection.java b/network/src/main/java/io/bitsquare/p2p/network/Connection.java index 6e5521c50c..34a822a761 100644 --- a/network/src/main/java/io/bitsquare/p2p/network/Connection.java +++ b/network/src/main/java/io/bitsquare/p2p/network/Connection.java @@ -62,7 +62,7 @@ public class Connection implements MessageListener { /////////////////////////////////////////////////////////////////////////////////////////// private final Socket socket; - private final MessageListener messageListener; + // private final MessageListener messageListener; private final ConnectionListener connectionListener; private final String portInfo; private final String uid = UUID.randomUUID().toString(); @@ -85,6 +85,8 @@ public class Connection implements MessageListener { private PeerType peerType; private final ObjectProperty nodeAddressProperty = new SimpleObjectProperty<>(); private List messageTimeStamps = new ArrayList<>(); + private final CopyOnWriteArraySet messageListeners = new CopyOnWriteArraySet<>(); + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -93,9 +95,11 @@ public class Connection implements MessageListener { Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener, @Nullable NodeAddress peersNodeAddress) { this.socket = socket; - this.messageListener = messageListener; + //this.messageListener = messageListener; this.connectionListener = connectionListener; + addMessageListener(messageListener); + sharedModel = new SharedModel(this, socket); if (socket.getLocalPort() == 0) @@ -143,7 +147,6 @@ public class Connection implements MessageListener { // API /////////////////////////////////////////////////////////////////////////////////////////// - // Called form various threads public void sendMessage(Message message) { if (!stopped) { @@ -190,6 +193,19 @@ public class Connection implements MessageListener { } } + public void addMessageListener(MessageListener messageListener) { + boolean isNewEntry = messageListeners.add(messageListener); + if (!isNewEntry) + log.warn("Try to add a messageListener which was already added."); + } + + public void removeMessageListener(MessageListener messageListener) { + boolean contained = messageListeners.remove(messageListener); + if (!contained) + log.debug("Try to remove a messageListener which was never added.\n\t" + + "That might happen because of async behaviour of CopyOnWriteArraySet"); + } + @SuppressWarnings("unused") public void reportIllegalRequest(RuleViolation ruleViolation) { sharedModel.reportInvalidRequest(ruleViolation); @@ -224,11 +240,11 @@ public class Connection implements MessageListener { // MessageListener implementation /////////////////////////////////////////////////////////////////////////////////////////// - // Only get non - CloseConnectionMessage messages + // Only receive non - CloseConnectionMessage messages @Override public void onMessage(Message message, Connection connection) { - // connection is null as we get called from InputHandler, which does not hold a reference to Connection - UserThread.execute(() -> messageListener.onMessage(message, this)); + checkArgument(connection.equals(this)); + UserThread.execute(() -> messageListeners.stream().forEach(e -> e.onMessage(message, connection))); } diff --git a/network/src/main/java/io/bitsquare/p2p/peers/GetPeersRequestHandler.java b/network/src/main/java/io/bitsquare/p2p/peers/GetPeersRequestHandler.java new file mode 100644 index 0000000000..e546693de4 --- /dev/null +++ b/network/src/main/java/io/bitsquare/p2p/peers/GetPeersRequestHandler.java @@ -0,0 +1,122 @@ +package io.bitsquare.p2p.peers; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; +import io.bitsquare.app.Log; +import io.bitsquare.common.UserThread; +import io.bitsquare.p2p.network.CloseConnectionReason; +import io.bitsquare.p2p.network.Connection; +import io.bitsquare.p2p.network.NetworkNode; +import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; +import io.bitsquare.p2p.peers.messages.peers.GetPeersResponse; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Timer; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkArgument; + +public class GetPeersRequestHandler { + private static final Logger log = LoggerFactory.getLogger(GetPeersRequestHandler.class); + + private static final long TIME_OUT_SEC = 20; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Listener + /////////////////////////////////////////////////////////////////////////////////////////// + + public interface Listener { + void onComplete(); + + void onFault(String errorMessage, Connection connection); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Class fields + /////////////////////////////////////////////////////////////////////////////////////////// + + private final NetworkNode networkNode; + private final PeerManager peerManager; + private final Listener listener; + private Timer timeoutTimer; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + public GetPeersRequestHandler(NetworkNode networkNode, PeerManager peerManager, Listener listener) { + this.networkNode = networkNode; + this.peerManager = peerManager; + this.listener = listener; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void process(GetPeersRequest getPeersRequest, final Connection connection) { + Log.traceCall("getPeersRequest=" + getPeersRequest + "\n\tconnection=" + connection + "\n\tthis=" + this); + + checkArgument(connection.getPeersNodeAddressOptional().isPresent(), + "The peers address must have been already set at the moment"); + GetPeersResponse getPeersResponse = new GetPeersResponse(getPeersRequest.nonce, + peerManager.getConnectedPeersNonSeedNodes(connection.getPeersNodeAddressOptional().get())); + SettableFuture future = networkNode.sendMessage(connection, + getPeersResponse); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(Connection connection) { + log.trace("GetPeersResponse sent successfully"); + cleanup(); + listener.onComplete(); + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + String errorMessage = "Sending getPeersRequest to " + connection + + " failed. That is expected if the peer is offline. getPeersRequest=" + getPeersRequest + "." + + "Exception: " + throwable.getMessage(); + log.info(errorMessage); + handleFault(errorMessage, CloseConnectionReason.SEND_MSG_FAILURE, connection); + } + }); + + checkArgument(timeoutTimer == null, "onGetPeersRequest must not be called twice."); + timeoutTimer = UserThread.runAfter(() -> { + String errorMessage = "A timeout occurred at sending getPeersResponse:" + getPeersResponse + " on connection:" + connection; + log.info(errorMessage + " / PeerExchangeHandshake=" + + GetPeersRequestHandler.this); + log.info("timeoutTimer called. this=" + this); + handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection); + }, + TIME_OUT_SEC, TimeUnit.SECONDS); + + peerManager.addToReportedPeers(getPeersRequest.reportedPeers, connection); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void handleFault(String errorMessage, CloseConnectionReason sendMsgFailure, Connection connection) { + // TODO retry + cleanup(); + peerManager.shutDownConnection(connection, sendMsgFailure); + listener.onFault(errorMessage, connection); + } + + private void cleanup() { + if (timeoutTimer != null) { + timeoutTimer.cancel(); + timeoutTimer = null; + } + } +} diff --git a/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceHandshake.java b/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceHandshake.java index b1727bd2e3..5eb277788a 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceHandshake.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceHandshake.java @@ -117,11 +117,8 @@ public class MaintenanceHandshake implements MessageListener { Log.traceCall("getPeersRequest=" + getPeersRequest + "\n\tconnection=" + connection + "\n\tthis=" + this); HashSet reportedPeers = getPeersRequest.reportedPeers; - - /* StringBuilder result = new StringBuilder("Received peers:"); - reportedPeers.stream().forEach(e -> result.append("\n\t").append(e)); - log.trace(result.toString());*/ - log.trace("reportedPeers.size=" + reportedPeers.size()); + + peerManager.printReportedPeers(reportedPeers); checkArgument(connection.getPeersNodeAddressOptional().isPresent(), "The peers address must have been already set at the moment"); diff --git a/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceManager.java b/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceManager.java index f0d4ebcca5..0a60237a50 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/MaintenanceManager.java @@ -7,7 +7,6 @@ import io.bitsquare.common.util.Utilities; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.network.*; -import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,16 +17,17 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; public class MaintenanceManager implements MessageListener, ConnectionListener { private static final Logger log = LoggerFactory.getLogger(MaintenanceManager.class); + private static final int MAINTENANCE_DELAY_SEC = 5 * 60; + private final NetworkNode networkNode; private final PeerManager peerManager; private final Set seedNodeAddresses; - private final ScheduledThreadPoolExecutor executor; - private final Map peerExchangeHandshakeMap = new HashMap<>(); + private ScheduledThreadPoolExecutor executor; + private final Map peerExchangeHandshakeMap = new HashMap<>(); private Timer connectToMorePeersTimer, maintainConnectionsTimer; private boolean shutDownInProgress; @@ -42,7 +42,6 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { checkArgument(!seedNodeAddresses.isEmpty(), "seedNodeAddresses must not be empty"); this.seedNodeAddresses = new HashSet<>(seedNodeAddresses); - executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 10, 5); networkNode.addMessageListener(this); } @@ -53,8 +52,10 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { networkNode.removeMessageListener(this); stopConnectToMorePeersTimer(); stopMaintainConnectionsTimer(); - peerExchangeHandshakeMap.values().stream().forEach(PeerExchangeHandshake::closeHandshake); - MoreExecutors.shutdownAndAwaitTermination(executor, 500, TimeUnit.MILLISECONDS); + peerExchangeHandshakeMap.values().stream().forEach(PeerExchangeHandler::cleanup); + + if (executor != null) + MoreExecutors.shutdownAndAwaitTermination(executor, 100, TimeUnit.MILLISECONDS); } @@ -62,19 +63,15 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { // API /////////////////////////////////////////////////////////////////////////////////////////// - public void requestReportedPeersFromSeedNodes(NodeAddress nodeAddress) { - checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at requestReportedPeers"); - ArrayList remainingNodeAddresses = new ArrayList<>(seedNodeAddresses); - remainingNodeAddresses.remove(nodeAddress); - Collections.shuffle(remainingNodeAddresses); - requestReportedPeers(nodeAddress, remainingNodeAddresses); - - int delay = new Random().nextInt(60) + 60 * 3; // 3-4 min - executor.scheduleAtFixedRate(() -> UserThread.execute(this::maintainConnections), - delay, delay, TimeUnit.SECONDS); + public void start() { + if (executor == null) { + executor = Utilities.getScheduledThreadPoolExecutor("MaintenanceManager", 1, 2, 5); + int delay = new Random().nextInt(120) + MAINTENANCE_DELAY_SEC; // add 1-2 min. randomness + executor.scheduleAtFixedRate(() -> UserThread.execute(this::maintainConnections), + delay, delay, TimeUnit.SECONDS); + } } - /////////////////////////////////////////////////////////////////////////////////////////// // ConnectionListener implementation /////////////////////////////////////////////////////////////////////////////////////////// @@ -85,13 +82,13 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { - // We use a timer to throttle if we get a series of disconnects + /* // We use a timer to throttle if we get a series of disconnects // The more connections we have the more relaxed we are with a checkConnections stopMaintainConnectionsTimer(); int size = networkNode.getAllConnections().size(); int delay = 10 + 2 * size * size; // 12 sec - 210 sec (3.5 min) maintainConnectionsTimer = UserThread.runAfter(this::maintainConnections, - delay, TimeUnit.SECONDS); + delay, TimeUnit.SECONDS);*/ } @Override @@ -105,7 +102,7 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { @Override public void onMessage(Message message, Connection connection) { - if (message instanceof GetPeersRequest) { + /* if (message instanceof GetPeersRequest) { Log.traceCall(message.toString() + "\n\tconnection=" + connection); PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, peerManager, @@ -123,7 +120,7 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { } }); peerExchangeHandshake.onGetPeersRequest((GetPeersRequest) message, connection); - } + }*/ } @@ -134,9 +131,9 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { private void requestReportedPeers(NodeAddress nodeAddress, List remainingNodeAddresses) { Log.traceCall("nodeAddress=" + nodeAddress); if (!peerExchangeHandshakeMap.containsKey(nodeAddress)) { - PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, + PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode, peerManager, - new PeerExchangeHandshake.Listener() { + new PeerExchangeHandler.Listener() { @Override public void onComplete() { log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress); @@ -167,8 +164,8 @@ public class MaintenanceManager implements MessageListener, ConnectionListener { } } }); - peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandshake); - peerExchangeHandshake.requestConnectedPeers(nodeAddress); + peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandler); + peerExchangeHandler.requestConnectedPeers(nodeAddress); } else { //TODO check when that happens log.warn("We have started already a peerExchangeHandshake. " + diff --git a/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandler.java b/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandler.java new file mode 100644 index 0000000000..ed3000bfd2 --- /dev/null +++ b/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandler.java @@ -0,0 +1,158 @@ +package io.bitsquare.p2p.peers; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; +import io.bitsquare.app.Log; +import io.bitsquare.common.UserThread; +import io.bitsquare.p2p.Message; +import io.bitsquare.p2p.NodeAddress; +import io.bitsquare.p2p.network.CloseConnectionReason; +import io.bitsquare.p2p.network.Connection; +import io.bitsquare.p2p.network.MessageListener; +import io.bitsquare.p2p.network.NetworkNode; +import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; +import io.bitsquare.p2p.peers.messages.peers.GetPeersResponse; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.Random; +import java.util.Timer; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +public class PeerExchangeHandler implements MessageListener { + private static final Logger log = LoggerFactory.getLogger(PeerExchangeHandler.class); + + private static final long TIME_OUT_SEC = 20; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Listener + /////////////////////////////////////////////////////////////////////////////////////////// + + public interface Listener { + void onComplete(); + + void onFault(String errorMessage, @Nullable Connection connection); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Class fields + /////////////////////////////////////////////////////////////////////////////////////////// + + private final NetworkNode networkNode; + private final PeerManager peerManager; + private final Listener listener; + private final long nonce = new Random().nextLong(); + private Timer timeoutTimer; + public Connection connection; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// + + public PeerExchangeHandler(NetworkNode networkNode, PeerManager peerManager, Listener listener) { + this.networkNode = networkNode; + this.peerManager = peerManager; + this.listener = listener; + + //networkNode.addMessageListener(this); + } + + public void cleanup() { + if (connection != null) + connection.removeMessageListener(this); + + if (timeoutTimer != null) { + timeoutTimer.cancel(); + timeoutTimer = null; + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + + public void requestConnectedPeers(NodeAddress nodeAddress) { + Log.traceCall("nodeAddress=" + nodeAddress + " / this=" + this); + checkNotNull(networkNode.getNodeAddress(), "PeerExchangeHandshake.requestReportedPeers: My node address must " + + "not be null at requestReportedPeers"); + GetPeersRequest getPeersRequest = new GetPeersRequest(networkNode.getNodeAddress(), nonce, peerManager.getConnectedPeersNonSeedNodes(nodeAddress)); + SettableFuture future = networkNode.sendMessage(nodeAddress, getPeersRequest); + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(Connection connection) { + PeerExchangeHandler.this.connection = connection; + connection.addMessageListener(PeerExchangeHandler.this); + log.trace("Send " + getPeersRequest + " to " + nodeAddress + " succeeded."); + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + String errorMessage = "Sending getPeersRequest to " + nodeAddress + + " failed. That is expected if the peer is offline.\n\tgetPeersRequest=" + getPeersRequest + + ".\n\tException=" + throwable.getMessage(); + log.info(errorMessage); + handleFault(errorMessage, CloseConnectionReason.SEND_MSG_FAILURE, nodeAddress); + } + }); + + checkArgument(timeoutTimer == null, "requestReportedPeers must not be called twice."); + timeoutTimer = UserThread.runAfter(() -> { + String errorMessage = "A timeout occurred at sending getPeersRequest:" + getPeersRequest + " for nodeAddress:" + nodeAddress; + log.info(errorMessage + " / PeerExchangeHandshake=" + + PeerExchangeHandler.this); + log.info("timeoutTimer called on " + this); + handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, nodeAddress); + }, + TIME_OUT_SEC, TimeUnit.SECONDS); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // MessageListener implementation + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onMessage(Message message, Connection connection) { + if (message instanceof GetPeersResponse) { + GetPeersResponse getPeersResponse = (GetPeersResponse) message; + // Check if the response is for our request + if (getPeersResponse.requestNonce == nonce) { + Log.traceCall(message.toString() + "\n\tconnection=" + connection); + Log.traceCall("this=" + this); + peerManager.addToReportedPeers(getPeersResponse.reportedPeers, connection); + + cleanup(); + listener.onComplete(); + } else { + log.trace("Nonce not matching. That message is not intended for us.\n\t" + + "We drop that message. nonce={} / requestNonce={}", + nonce, getPeersResponse.requestNonce); + } + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void handleFault(String errorMessage, CloseConnectionReason sendMsgFailure, NodeAddress nodeAddress) { + // TODO retry + cleanup(); + if (connection == null) + peerManager.shutDownConnection(nodeAddress, sendMsgFailure); + else + peerManager.shutDownConnection(connection, sendMsgFailure); + + listener.onFault(errorMessage, connection); + } +} diff --git a/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandshake.java b/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandshake.java deleted file mode 100644 index 56188573cb..0000000000 --- a/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeHandshake.java +++ /dev/null @@ -1,219 +0,0 @@ -package io.bitsquare.p2p.peers; - -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.SettableFuture; -import io.bitsquare.app.Log; -import io.bitsquare.common.UserThread; -import io.bitsquare.p2p.Message; -import io.bitsquare.p2p.NodeAddress; -import io.bitsquare.p2p.network.CloseConnectionReason; -import io.bitsquare.p2p.network.Connection; -import io.bitsquare.p2p.network.MessageListener; -import io.bitsquare.p2p.network.NetworkNode; -import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; -import io.bitsquare.p2p.peers.messages.peers.GetPeersResponse; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; -import java.util.HashSet; -import java.util.Random; -import java.util.Timer; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -public class PeerExchangeHandshake implements MessageListener { - private static final Logger log = LoggerFactory.getLogger(PeerExchangeHandshake.class); - - /////////////////////////////////////////////////////////////////////////////////////////// - // Listener - /////////////////////////////////////////////////////////////////////////////////////////// - - public interface Listener { - void onComplete(); - - void onFault(String errorMessage, @Nullable Connection connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Class fields - /////////////////////////////////////////////////////////////////////////////////////////// - - private final NetworkNode networkNode; - private final PeerManager peerManager; - private final Listener listener; - private final long nonce = new Random().nextLong(); - private Timer timeoutTimer; - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor - /////////////////////////////////////////////////////////////////////////////////////////// - - public PeerExchangeHandshake(NetworkNode networkNode, PeerManager peerManager, Listener listener) { - this.networkNode = networkNode; - this.peerManager = peerManager; - this.listener = listener; - - networkNode.addMessageListener(this); - } - - public void closeHandshake() { - networkNode.removeMessageListener(this); - stopTimeoutTimer(); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void requestConnectedPeers(NodeAddress nodeAddress) { - Log.traceCall("nodeAddress=" + nodeAddress + " / this=" + this); - checkNotNull(networkNode.getNodeAddress(), "PeerExchangeHandshake.requestReportedPeers: My node address must " + - "not be null at requestReportedPeers"); - GetPeersRequest getPeersRequest = new GetPeersRequest(networkNode.getNodeAddress(), nonce, getConnectedPeers(nodeAddress)); - SettableFuture future = networkNode.sendMessage(nodeAddress, getPeersRequest); - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Connection connection) { - log.trace("Send " + getPeersRequest + " to " + nodeAddress + " succeeded."); - } - - @Override - public void onFailure(@NotNull Throwable throwable) { - String errorMessage = "Sending getPeersRequest to " + nodeAddress + - " failed. That is expected if the peer is offline.\n\tgetPeersRequest=" + getPeersRequest + - ".\n\tException=" + throwable.getMessage(); - log.info(errorMessage); - - peerManager.shutDownConnection(nodeAddress, CloseConnectionReason.SEND_MSG_FAILURE); - closeHandshake(); - listener.onFault(errorMessage, null); - } - }); - - checkArgument(timeoutTimer == null, "requestReportedPeers must not be called twice."); - timeoutTimer = UserThread.runAfter(() -> { - String errorMessage = "A timeout occurred at sending getPeersRequest:" + getPeersRequest + " for nodeAddress:" + nodeAddress; - log.info(errorMessage + " / PeerExchangeHandshake=" + - PeerExchangeHandshake.this); - - log.info("timeoutTimer called on " + this); - peerManager.shutDownConnection(nodeAddress, CloseConnectionReason.SEND_MSG_TIMEOUT); - closeHandshake(); - listener.onFault(errorMessage, null); - }, - 20, TimeUnit.SECONDS); - } - - public void onGetPeersRequest(GetPeersRequest getPeersRequest, final Connection connection) { - Log.traceCall("getPeersRequest=" + getPeersRequest + "\n\tconnection=" + connection + "\n\tthis=" + this); - - HashSet reportedPeers = getPeersRequest.reportedPeers; - - /* StringBuilder result = new StringBuilder("Received peers:"); - reportedPeers.stream().forEach(e -> result.append("\n\t").append(e)); - log.trace(result.toString());*/ - log.trace("reportedPeers.size=" + reportedPeers.size()); - - checkArgument(connection.getPeersNodeAddressOptional().isPresent(), - "The peers address must have been already set at the moment"); - GetPeersResponse getPeersResponse = new GetPeersResponse(getPeersRequest.nonce, - getConnectedPeers(connection.getPeersNodeAddressOptional().get())); - SettableFuture future = networkNode.sendMessage(connection, - getPeersResponse); - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Connection connection) { - log.trace("GetPeersResponse sent successfully"); - closeHandshake(); - listener.onComplete(); - } - - @Override - public void onFailure(@NotNull Throwable throwable) { - String errorMessage = "Sending getPeersRequest to " + connection + - " failed. That is expected if the peer is offline. getPeersRequest=" + getPeersRequest + "." + - "Exception: " + throwable.getMessage(); - log.info(errorMessage); - - peerManager.shutDownConnection(connection, CloseConnectionReason.SEND_MSG_FAILURE); - closeHandshake(); - listener.onFault(errorMessage, connection); - } - }); - - checkArgument(timeoutTimer == null, "onGetPeersRequest must not be called twice."); - timeoutTimer = UserThread.runAfter(() -> { - String errorMessage = "A timeout occurred at sending getPeersResponse:" + getPeersResponse + " on connection:" + connection; - log.info(errorMessage + " / PeerExchangeHandshake=" + - PeerExchangeHandshake.this); - - log.info("timeoutTimer called. this=" + this); - peerManager.shutDownConnection(connection, CloseConnectionReason.SEND_MSG_TIMEOUT); - closeHandshake(); - listener.onFault(errorMessage, connection); - }, - 20, TimeUnit.SECONDS); - - peerManager.addToReportedPeers(reportedPeers, connection); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // MessageListener implementation - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void onMessage(Message message, Connection connection) { - if (message instanceof GetPeersResponse) { - Log.traceCall(message.toString() + "\n\tconnection=" + connection); - Log.traceCall("this=" + this); - GetPeersResponse getPeersResponse = (GetPeersResponse) message; - if (getPeersResponse.requestNonce == nonce) { - stopTimeoutTimer(); - - HashSet reportedPeers = getPeersResponse.reportedPeers; - StringBuilder result = new StringBuilder("Received peers:"); - reportedPeers.stream().forEach(e -> result.append("\n\t").append(e)); - log.trace(result.toString()); - peerManager.addToReportedPeers(reportedPeers, connection); - - closeHandshake(); - listener.onComplete(); - } else { - log.debug("Nonce not matching. That can happen rarely if we get a response after a canceled handshake " + - "(timeout causes connection close but peer might have sent a msg before connection " + - "was closed).\n\tWe drop that message. nonce={} / requestNonce={}", - nonce, getPeersResponse.requestNonce); - } - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private HashSet getConnectedPeers(NodeAddress receiverNodeAddress) { - return new HashSet<>(peerManager.getConnectedPeers().stream() - .filter(e -> !peerManager.isSeedNode(e) && - !e.nodeAddress.equals(receiverNodeAddress) - ) - .collect(Collectors.toSet())); - } - - private void stopTimeoutTimer() { - if (timeoutTimer != null) { - timeoutTimer.cancel(); - timeoutTimer = null; - } - } -} diff --git a/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeManager.java b/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeManager.java index ae6c24ce76..49e7481382 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/PeerExchangeManager.java @@ -1,16 +1,20 @@ package io.bitsquare.p2p.peers; +import com.google.common.util.concurrent.MoreExecutors; import io.bitsquare.app.Log; import io.bitsquare.common.UserThread; +import io.bitsquare.common.util.Utilities; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.network.*; import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; -import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.util.*; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -19,13 +23,17 @@ import static com.google.common.base.Preconditions.checkNotNull; public class PeerExchangeManager implements MessageListener, ConnectionListener { private static final Logger log = LoggerFactory.getLogger(PeerExchangeManager.class); + private static final long RETRY_DELAY_SEC = 60; + private static final long MAINTENANCE_DELAY_SEC = 5; + private final NetworkNode networkNode; private final PeerManager peerManager; private final Set seedNodeAddresses; - private final Map peerExchangeHandshakeMap = new HashMap<>(); + private final Map peerExchangeHandlerMap = new HashMap<>(); private Timer connectToMorePeersTimer; private boolean shutDownInProgress; - + private ScheduledThreadPoolExecutor executor; + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -38,15 +46,19 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener this.seedNodeAddresses = new HashSet<>(seedNodeAddresses); networkNode.addMessageListener(this); + networkNode.addConnectionListener(this); } public void shutDown() { Log.traceCall(); shutDownInProgress = true; - networkNode.removeMessageListener(this); + networkNode.removeConnectionListener(this); stopConnectToMorePeersTimer(); - peerExchangeHandshakeMap.values().stream().forEach(PeerExchangeHandshake::closeHandshake); + peerExchangeHandlerMap.values().stream().forEach(PeerExchangeHandler::cleanup); + + if (executor != null) + MoreExecutors.shutdownAndAwaitTermination(executor, 100, TimeUnit.MILLISECONDS); } @@ -60,8 +72,13 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener remainingNodeAddresses.remove(nodeAddress); Collections.shuffle(remainingNodeAddresses); requestReportedPeers(nodeAddress, remainingNodeAddresses); - } + if (executor == null) { + executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 2, 5); + executor.scheduleAtFixedRate(() -> UserThread.execute(this::requestAgain), + MAINTENANCE_DELAY_SEC, MAINTENANCE_DELAY_SEC, TimeUnit.SECONDS); + } + } /////////////////////////////////////////////////////////////////////////////////////////// // ConnectionListener implementation @@ -73,6 +90,12 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener @Override public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { + if (connectToMorePeersTimer == null) + connectToMorePeersTimer = UserThread.runAfter(() -> { + log.trace("ConnectToMorePeersTimer called from onDisconnect code path"); + stopConnectToMorePeersTimer(); + requestWithAvailablePeers(); + }, RETRY_DELAY_SEC); } @Override @@ -88,41 +111,41 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener public void onMessage(Message message, Connection connection) { if (message instanceof GetPeersRequest) { Log.traceCall(message.toString() + "\n\tconnection=" + connection); - PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, + GetPeersRequestHandler getPeersRequestHandler = new GetPeersRequestHandler(networkNode, peerManager, - new PeerExchangeHandshake.Listener() { + new GetPeersRequestHandler.Listener() { @Override public void onComplete() { log.trace("PeerExchangeHandshake of inbound connection complete.\n\tConnection={}", connection); } @Override - public void onFault(String errorMessage, @Nullable Connection connection) { + public void onFault(String errorMessage, Connection connection) { log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" + "connection={}", errorMessage, connection); peerManager.handleConnectionFault(connection); } }); - peerExchangeHandshake.onGetPeersRequest((GetPeersRequest) message, connection); + getPeersRequestHandler.process((GetPeersRequest) message, connection); } } /////////////////////////////////////////////////////////////////////////////////////////// - // Private + // Request /////////////////////////////////////////////////////////////////////////////////////////// private void requestReportedPeers(NodeAddress nodeAddress, List remainingNodeAddresses) { Log.traceCall("nodeAddress=" + nodeAddress); - if (!peerExchangeHandshakeMap.containsKey(nodeAddress)) { - PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, + if (!peerExchangeHandlerMap.containsKey(nodeAddress)) { + PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode, peerManager, - new PeerExchangeHandshake.Listener() { + new PeerExchangeHandler.Listener() { @Override public void onComplete() { log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress); - peerExchangeHandshakeMap.remove(nodeAddress); - connectToMorePeers(); + peerExchangeHandlerMap.remove(nodeAddress); + requestWithAvailablePeers(); } @Override @@ -130,26 +153,36 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" + "nodeAddress={}", errorMessage, nodeAddress); - peerExchangeHandshakeMap.remove(nodeAddress); + peerExchangeHandlerMap.remove(nodeAddress); peerManager.handleConnectionFault(nodeAddress, connection); if (!shutDownInProgress) { if (!remainingNodeAddresses.isEmpty()) { - log.info("There are remaining nodes available for requesting peers. " + - "We will try getReportedPeers again."); - requestReportedPeersFromRandomPeer(remainingNodeAddresses); + if (!peerManager.hasSufficientConnections()) { + log.info("There are remaining nodes available for requesting peers. " + + "We will try getReportedPeers again."); + NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size())); + remainingNodeAddresses.remove(nextCandidate); + requestReportedPeers(nextCandidate, remainingNodeAddresses); + } else { + // That path will rarely be reached + log.info("We have already sufficient connections."); + } } else { log.info("There is no remaining node available for requesting peers. " + "That is expected if no other node is online.\n\t" + - "We will try again after a random pause."); + "We will try again after a pause."); if (connectToMorePeersTimer == null) - connectToMorePeersTimer = UserThread.runAfterRandomDelay( - PeerExchangeManager.this::connectToMorePeers, 20, 30); + connectToMorePeersTimer = UserThread.runAfter(() -> { + log.trace("ConnectToMorePeersTimer called from requestReportedPeers code path"); + stopConnectToMorePeersTimer(); + requestWithAvailablePeers(); + }, RETRY_DELAY_SEC); } } } }); - peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandshake); - peerExchangeHandshake.requestConnectedPeers(nodeAddress); + peerExchangeHandlerMap.put(nodeAddress, peerExchangeHandler); + peerExchangeHandler.requestConnectedPeers(nodeAddress); } else { //TODO check when that happens log.warn("We have started already a peerExchangeHandshake. " + @@ -158,59 +191,86 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener } } - - private void connectToMorePeers() { + private void requestWithAvailablePeers() { Log.traceCall(); - stopConnectToMorePeersTimer(); - if (!peerManager.hasSufficientConnections()) { // We create a new list of not connected candidates - // 1. reported sorted by most recent lastActivityDate - // 2. persisted sorted by most recent lastActivityDate - // 3. seenNodes - List list = new ArrayList<>(getFilteredAndSortedList(peerManager.getReportedPeers(), new ArrayList<>())); - list.addAll(getFilteredAndSortedList(peerManager.getPersistedPeers(), list)); - ArrayList seedNodeAddresses = new ArrayList<>(this.seedNodeAddresses); - Collections.shuffle(seedNodeAddresses); - list.addAll(seedNodeAddresses.stream() - .filter(e -> !list.contains(e) && - !peerManager.isSelf(e) && - !peerManager.isConfirmed(e)) - .collect(Collectors.toSet())); - log.info("Sorted and filtered list: list.size()=" + list.size()); - log.trace("Sorted and filtered list: list=" + list); + // 1. reported shuffled peers + // 2. persisted shuffled peers + // 3. Add as last shuffled seedNodes (least priority) + List list = getFilteredNonSeedNodeList(getNodeAddresses(peerManager.getReportedPeers()), new ArrayList<>()); + Collections.shuffle(list); + + List filteredPersistedPeers = getFilteredNonSeedNodeList(getNodeAddresses(peerManager.getPersistedPeers()), list); + Collections.shuffle(filteredPersistedPeers); + list.addAll(filteredPersistedPeers); + + List filteredSeedNodeAddresses = getFilteredList(new ArrayList<>(seedNodeAddresses), list); + Collections.shuffle(filteredSeedNodeAddresses); + list.addAll(filteredSeedNodeAddresses); + + log.info("Number of peers in list for connectToMorePeers: {}", list.size()); + log.trace("Filtered connectToMorePeers list: list=" + list); if (!list.isEmpty()) { + // Dont shuffle as we want the seed nodes at the last entries NodeAddress nextCandidate = list.get(0); list.remove(nextCandidate); requestReportedPeers(nextCandidate, list); } else { - log.info("No more peers are available for requestReportedPeers."); + log.info("No more peers are available for requestReportedPeers. We will try again after a pause."); + if (connectToMorePeersTimer == null) + connectToMorePeersTimer = UserThread.runAfter(() -> { + log.trace("ConnectToMorePeersTimer called from requestWithAvailablePeers code path"); + stopConnectToMorePeersTimer(); + requestWithAvailablePeers(); + }, RETRY_DELAY_SEC); } } else { log.info("We have already sufficient connections."); } } - // sorted by most recent lastActivityDate - private List getFilteredAndSortedList(Set set, List list) { - return set.stream() - .filter(e -> !list.contains(e.nodeAddress) && - !peerManager.isSeedNode(e) && - !peerManager.isSelf(e) && - !peerManager.isConfirmed(e)) - .collect(Collectors.toList()) - .stream() - .filter(e -> e.lastActivityDate != null) - .sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate)) + + /////////////////////////////////////////////////////////////////////////////////////////// + // Maintenance + /////////////////////////////////////////////////////////////////////////////////////////// + + private void requestAgain() { + checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at sendUpdateRequest"); + Set candidates = new HashSet<>(getNodeAddresses(peerManager.getReportedPeers())); + candidates.addAll(getNodeAddresses(peerManager.getPersistedPeers())); + candidates.addAll(seedNodeAddresses); + candidates.remove(networkNode.getNodeAddress()); + ArrayList list = new ArrayList<>(candidates); + Collections.shuffle(list); + NodeAddress candidate = list.remove(0); + requestReportedPeers(candidate, list); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Utils + /////////////////////////////////////////////////////////////////////////////////////////// + + private List getNodeAddresses(Collection collection) { + return collection.stream() .map(e -> e.nodeAddress) .collect(Collectors.toList()); } - private void requestReportedPeersFromRandomPeer(List remainingNodeAddresses) { - NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size())); - remainingNodeAddresses.remove(nextCandidate); - requestReportedPeers(nextCandidate, remainingNodeAddresses); + private List getFilteredList(Collection collection, List list) { + return collection.stream() + .filter(e -> !list.contains(e) && + !peerManager.isSelf(e) && + !peerManager.isConfirmed(e)) + .collect(Collectors.toList()); + } + + private List getFilteredNonSeedNodeList(Collection collection, List list) { + return getFilteredList(collection, list).stream() + .filter(e -> !peerManager.isSeedNode(e)) + .collect(Collectors.toList()); } private void stopConnectToMorePeersTimer() { diff --git a/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java b/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java index a7a854601e..2993dddee7 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/PeerManager.java @@ -15,7 +15,6 @@ import javax.annotation.Nullable; import java.io.File; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; @@ -32,6 +31,7 @@ public class PeerManager implements ConnectionListener, MessageListener { private static int MAX_CONNECTIONS_EXTENDED_1; private static int MAX_CONNECTIONS_EXTENDED_2; private static int MAX_CONNECTIONS_EXTENDED_3; + private boolean printReportedPeersDetails = true; public static void setMaxConnections(int maxConnections) { MAX_CONNECTIONS = maxConnections; @@ -271,87 +271,48 @@ public class PeerManager implements ConnectionListener, MessageListener { } public void addToReportedPeers(HashSet reportedPeersToAdd, Connection connection) { - // we disconnect misbehaving nodes trying to send too many peers - // reported peers include the connected peers which is normally max. 10 but we give some headroom - // for safety - if (reportedPeersToAdd.size() > (MAX_REPORTED_PEERS + PeerManager.MIN_CONNECTIONS * 3)) { - // Will trigger a shutdown after 2nd time sending too much - connection.reportIllegalRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); - } else { - // In case we have one of the peers already we adjust the lastActivityDate by adjusting the date to the mid - // of the lastActivityDate of our already stored peer and the reported one - Map reportedPeersMap = reportedPeers.stream() - .collect(Collectors.toMap(e -> e, Function.identity())); - HashSet adjustedReportedPeers = new HashSet<>(); - reportedPeersToAdd.stream() - .filter(e -> e.nodeAddress != null && - !e.nodeAddress.equals(networkNode.getNodeAddress()) && - !getConnectedPeers().contains(e)) - .forEach(e -> { - if (reportedPeersMap.containsKey(e)) { - if (e.lastActivityDate != null && reportedPeersMap.get(e).lastActivityDate != null) { - long adjustedTime = (e.lastActivityDate.getTime() + - reportedPeersMap.get(e).lastActivityDate.getTime()) / 2; - adjustedReportedPeers.add(new ReportedPeer(e.nodeAddress, - new Date(adjustedTime))); - } else if (e.lastActivityDate == null) { - adjustedReportedPeers.add(reportedPeersMap.get(e)); - } else if (reportedPeersMap.get(e).lastActivityDate == null) { - adjustedReportedPeers.add(e); - } - } else { - adjustedReportedPeers.add(e); - } - }); - - reportedPeers.addAll(adjustedReportedPeers); + printReportedPeers(reportedPeersToAdd); + // We check if the reported msg is not violating our rules + if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + PeerManager.MAX_CONNECTIONS_EXTENDED_3 + 10)) { + reportedPeers.addAll(reportedPeersToAdd); purgeReportedPeersIfExceeds(); persistedPeers.addAll(reportedPeersToAdd); - persistedPeers.addAll(new HashSet<>(getConnectedPeers())); - - // We remove if we exceeds MAX_PERSISTED_PEERS limit - int toRemove = persistedPeers.size() - MAX_PERSISTED_PEERS; - if (toRemove > 0) { - int toRemove1 = toRemove / 2; - if (toRemove1 > 0) { - // we remove the first half randomly to avoid attack vectors with lastActivityDate - List list = new ArrayList<>(persistedPeers); - for (int i = 0; i < toRemove1; i++) { - persistedPeers.remove(list.get(i)); - } - int toRemove2 = toRemove - toRemove1; - if (toRemove2 > 0) { - // now we remove second half with a list sorted by oldest lastActivityDate - list = new ArrayList<>(persistedPeers); - list = list.stream().filter(e -> e.lastActivityDate != null).collect(Collectors.toList()); - list.sort((o1, o2) -> o1.lastActivityDate.compareTo(o2.lastActivityDate)); - for (int i = 0; i < toRemove2; i++) { - persistedPeers.remove(list.get(i)); - } - } - } - } - + purgePersistedPeersIfExceeds(); if (dbStorage != null) dbStorage.queueUpForSave(persistedPeers, 2000); - } - printReportedPeers(); + printReportedPeers(); + } else { + // If a node is trying to send too many peers we treat it as rule violation. + // Reported peers include the connected peers. We use the max value and give some extra headroom. + // Will trigger a shutdown after 2nd time sending too much + connection.reportIllegalRequest(RuleViolation.TOO_MANY_REPORTED_PEERS_SENT); + } } private void printReportedPeers() { if (!reportedPeers.isEmpty()) { - StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + - "Reported peers:"); - reportedPeers.stream().forEach(e -> result.append("\n").append(e)); - result.append("\n------------------------------------------------------------\n"); - //log.trace(result.toString()); - log.info("Number of reported peers: {}", reportedPeers.size()); + if (printReportedPeersDetails) { + StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + + "Collected reported peers:"); + reportedPeers.stream().forEach(e -> result.append("\n").append(e)); + result.append("\n------------------------------------------------------------\n"); + log.info(result.toString()); + } + log.info("Number of collected reported peers: {}", reportedPeers.size()); } } + public void printReportedPeers(HashSet reportedPeers) { + if (printReportedPeersDetails) { + StringBuilder result = new StringBuilder("We received now reportedPeers:"); + reportedPeers.stream().forEach(e -> result.append("\n\t").append(e)); + log.info(result.toString()); + } + log.info("Number of new arrived reported peers: {}", reportedPeers.size()); + } /////////////////////////////////////////////////////////////////////////////////////////// // Persisted peers @@ -455,6 +416,7 @@ public class PeerManager implements ConnectionListener, MessageListener { return isConfirmed(reportedPeer.nodeAddress); } + // Checks if that connection has the peers node address public boolean isConfirmed(NodeAddress nodeAddress) { return networkNode.getNodeAddressesOfConfirmedConnections().contains(nodeAddress); } @@ -480,22 +442,41 @@ public class PeerManager implements ConnectionListener, MessageListener { private void purgeReportedPeersIfExceeds() { Log.traceCall(); int size = getReportedPeers().size(); - if (size > MAX_REPORTED_PEERS) { - log.trace("We have more then {} reported peers. size={}. " + - "We remove random peers from the reported peers list.", MAX_REPORTED_PEERS, size); - int diff = size - MAX_REPORTED_PEERS; + int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_EXTENDED_3; + if (size > limit) { + log.trace("We have already {} reported peers which exceeds our limit of {}." + + "We remove random peers from the reported peers list.", size, limit); + int diff = size - limit; List list = new ArrayList<>(getReportedPeers()); // we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random for (int i = 0; i < diff; i++) { ReportedPeer toRemove = getAndRemoveRandomReportedPeer(list); removeReportedPeer(toRemove); - removePersistedPeer(toRemove); } } else { log.trace("No need to purge reported peers.\n\tWe don't have more then {} reported peers yet.", MAX_REPORTED_PEERS); } } + private void purgePersistedPeersIfExceeds() { + Log.traceCall(); + int size = getPersistedPeers().size(); + int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_EXTENDED_3; + if (size > limit) { + log.trace("We have already {} persisted peers which exceeds our limit of {}." + + "We remove random peers from the persisted peers list.", size, limit); + int diff = size - limit; + List list = new ArrayList<>(getReportedPeers()); + // we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random + for (int i = 0; i < diff; i++) { + ReportedPeer toRemove = getAndRemoveRandomReportedPeer(list); + removePersistedPeer(toRemove); + } + } else { + log.trace("No need to purge persisted peers.\n\tWe don't have more then {} persisted peers yet.", MAX_REPORTED_PEERS); + } + } + private ReportedPeer getAndRemoveRandomReportedPeer(List list) { checkArgument(!list.isEmpty(), "List must not be empty"); return list.remove(new Random().nextInt(list.size())); @@ -509,6 +490,18 @@ public class PeerManager implements ConnectionListener, MessageListener { .collect(Collectors.toSet()); } + public HashSet getConnectedPeersNonSeedNodes() { + return new HashSet<>(getConnectedPeers().stream() + .filter(e -> !isSeedNode(e)) + .collect(Collectors.toSet())); + } + + public HashSet getConnectedPeersNonSeedNodes(NodeAddress excludedNodeAddress) { + return new HashSet<>(getConnectedPeersNonSeedNodes().stream() + .filter(e -> !e.nodeAddress.equals(excludedNodeAddress)) + .collect(Collectors.toSet())); + } + private void stopCheckMaxConnectionsTimer() { if (checkMaxConnectionsTimer != null) { checkMaxConnectionsTimer.cancel(); diff --git a/network/src/main/java/io/bitsquare/p2p/peers/RequestDataManager.java b/network/src/main/java/io/bitsquare/p2p/peers/RequestDataManager.java index 0f264a1947..512df54846 100644 --- a/network/src/main/java/io/bitsquare/p2p/peers/RequestDataManager.java +++ b/network/src/main/java/io/bitsquare/p2p/peers/RequestDataManager.java @@ -22,6 +22,9 @@ import static com.google.common.base.Preconditions.checkArgument; public class RequestDataManager implements MessageListener { private static final Logger log = LoggerFactory.getLogger(RequestDataManager.class); + private static final long RETRY_DELAY_SEC = 10; + + /////////////////////////////////////////////////////////////////////////////////////////// // Listener /////////////////////////////////////////////////////////////////////////////////////////// @@ -193,7 +196,7 @@ public class RequestDataManager implements MessageListener { // try again after a pause stopRequestDataTimer(); - requestDataTimer = UserThread.runAfterRandomDelay(() -> { + requestDataTimer = UserThread.runAfter(() -> { log.trace("requestDataAfterDelayTimer called"); // We want to keep it sorted but avoid duplicates // We don't filter out already established connections for seed nodes as it might be that @@ -208,7 +211,7 @@ public class RequestDataManager implements MessageListener { list.remove(nextCandidate); requestData(nextCandidate, list); }, - 10, 15, TimeUnit.SECONDS); + RETRY_DELAY_SEC, TimeUnit.SECONDS); } requestDataHandshakeMap.remove(nodeAddress); diff --git a/network/src/main/java/io/bitsquare/p2p/seed/SeedNodesRepository.java b/network/src/main/java/io/bitsquare/p2p/seed/SeedNodesRepository.java index e730249e53..0403bceda5 100644 --- a/network/src/main/java/io/bitsquare/p2p/seed/SeedNodesRepository.java +++ b/network/src/main/java/io/bitsquare/p2p/seed/SeedNodesRepository.java @@ -32,14 +32,12 @@ public class SeedNodesRepository { new NodeAddress("izs5oz7i5ta7c2ir.onion:8000"),*/ // v0.3.5 - new NodeAddress("que4ysbd2qazkb7d.onion:8000"), - new NodeAddress("h2crs2j5huhclkc6.onion:8000"), - new NodeAddress("7a3sj4j6yw5oukai.onion:8000"), + new NodeAddress("hulvbm5xjn7b7ku4.onion:8000"), + new NodeAddress("3efgjjbdvhbvck3x.onion:8000"), + new NodeAddress("3unfcshgwipxhxfm.onion:8000"), // testnet new NodeAddress("znmy44wcstn2rkva.onion:8001"), - /* new NodeAddress("zvn7umikgxml6x6h.onion:8001"), - new NodeAddress("wnfxmrmsyeeos2dy.onion:8001"),*/ // regtest // For development you need to change that to your local onion addresses diff --git a/network/src/main/java/io/bitsquare/p2p/storage/data/ProtectedData.java b/network/src/main/java/io/bitsquare/p2p/storage/data/ProtectedData.java index da5689cca1..deb0a44816 100644 --- a/network/src/main/java/io/bitsquare/p2p/storage/data/ProtectedData.java +++ b/network/src/main/java/io/bitsquare/p2p/storage/data/ProtectedData.java @@ -15,7 +15,10 @@ public class ProtectedData implements Serializable { private static final Logger log = LoggerFactory.getLogger(P2PDataStorage.class); public final ExpirableMessage expirableMessage; + + //TODO check if that field make sense as it is in expirableMessage.getTTL() transient public long ttl; + public final PublicKey ownerPubKey; public final int sequenceNumber; public final byte[] signature;