UI improvements, P2P network WIP

This commit is contained in:
Manfred Karrer 2016-02-14 12:58:30 +01:00
parent 615f5570c3
commit 850b6d209c
54 changed files with 684 additions and 532 deletions

View file

@ -1,7 +1,6 @@
package io.bitsquare.btc.pricefeed; package io.bitsquare.btc.pricefeed;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.providers.PriceProvider; import io.bitsquare.btc.pricefeed.providers.PriceProvider;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -44,7 +43,7 @@ class GetPriceRequest {
} }
private SettableFuture<MarketPrice> requestPrice(String currencyCode, PriceProvider provider, SettableFuture<MarketPrice> resultFuture) { private SettableFuture<MarketPrice> requestPrice(String currencyCode, PriceProvider provider, SettableFuture<MarketPrice> resultFuture) {
Log.traceCall(currencyCode); // Log.traceCall(currencyCode);
ListenableFuture<MarketPrice> future = executorService.submit(() -> { ListenableFuture<MarketPrice> future = executorService.submit(() -> {
Thread.currentThread().setName("requestPrice-" + provider.toString()); Thread.currentThread().setName("requestPrice-" + provider.toString());
return provider.getPrice(currencyCode); return provider.getPrice(currencyCode);

View file

@ -4,7 +4,6 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider; import io.bitsquare.btc.pricefeed.providers.BitcoinAveragePriceProvider;
import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider; import io.bitsquare.btc.pricefeed.providers.PoloniexPriceProvider;
import io.bitsquare.btc.pricefeed.providers.PriceProvider; import io.bitsquare.btc.pricefeed.providers.PriceProvider;
@ -140,7 +139,7 @@ public class MarketPriceFeed {
if (priceConsumer != null && currencyCode != null && type != null) { if (priceConsumer != null && currencyCode != null && type != null) {
if (cache.containsKey(currencyCode)) { if (cache.containsKey(currencyCode)) {
MarketPrice marketPrice = cache.get(currencyCode); MarketPrice marketPrice = cache.get(currencyCode);
log.debug("applyPrice type=" + type); //log.debug("applyPrice type=" + type);
priceConsumer.accept(marketPrice.getPrice(type)); priceConsumer.accept(marketPrice.getPrice(type));
} else { } else {
String errorMessage = "We don't have a price for currencyCode " + currencyCode; String errorMessage = "We don't have a price for currencyCode " + currencyCode;
@ -151,14 +150,14 @@ public class MarketPriceFeed {
} }
private void requestPrice(PriceProvider provider) { private void requestPrice(PriceProvider provider) {
Log.traceCall(); //Log.traceCall();
GetPriceRequest getPriceRequest = new GetPriceRequest(); GetPriceRequest getPriceRequest = new GetPriceRequest();
SettableFuture<MarketPrice> future = getPriceRequest.requestPrice(currencyCode, provider); SettableFuture<MarketPrice> future = getPriceRequest.requestPrice(currencyCode, provider);
Futures.addCallback(future, new FutureCallback<MarketPrice>() { Futures.addCallback(future, new FutureCallback<MarketPrice>() {
public void onSuccess(MarketPrice marketPrice) { public void onSuccess(MarketPrice marketPrice) {
UserThread.execute(() -> { UserThread.execute(() -> {
cache.put(marketPrice.currencyCode, marketPrice); cache.put(marketPrice.currencyCode, marketPrice);
log.debug("marketPrice updated " + marketPrice); //log.debug("marketPrice updated " + marketPrice);
priceConsumer.accept(marketPrice.getPrice(type)); priceConsumer.accept(marketPrice.getPrice(type));
}); });
} }
@ -170,7 +169,7 @@ public class MarketPriceFeed {
} }
private void requestAllPrices(PriceProvider provider, @Nullable Runnable resultHandler) { private void requestAllPrices(PriceProvider provider, @Nullable Runnable resultHandler) {
Log.traceCall(); // Log.traceCall();
GetPriceRequest getPriceRequest = new GetPriceRequest(); GetPriceRequest getPriceRequest = new GetPriceRequest();
SettableFuture<Map<String, MarketPrice>> future = getPriceRequest.requestAllPrices(provider); SettableFuture<Map<String, MarketPrice>> future = getPriceRequest.requestAllPrices(provider);
Futures.addCallback(future, new FutureCallback<Map<String, MarketPrice>>() { Futures.addCallback(future, new FutureCallback<Map<String, MarketPrice>>() {

View file

@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap; import com.google.gson.internal.LinkedTreeMap;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.MarketPrice; import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.http.HttpClient; import io.bitsquare.http.HttpClient;
import io.bitsquare.http.HttpException; import io.bitsquare.http.HttpException;
@ -45,7 +44,7 @@ public class BitcoinAveragePriceProvider implements PriceProvider {
@Override @Override
public MarketPrice getPrice(String currencyCode) throws IOException, HttpException { public MarketPrice getPrice(String currencyCode) throws IOException, HttpException {
Log.traceCall("currencyCode=" + currencyCode); //Log.traceCall("currencyCode=" + currencyCode);
JsonObject jsonObject = new JsonParser() JsonObject jsonObject = new JsonParser()
.parse(httpClient.requestWithGET(currencyCode)) .parse(httpClient.requestWithGET(currencyCode))
.getAsJsonObject(); .getAsJsonObject();

View file

@ -4,7 +4,6 @@ import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.internal.LinkedTreeMap; import com.google.gson.internal.LinkedTreeMap;
import io.bitsquare.app.Log;
import io.bitsquare.btc.pricefeed.MarketPrice; import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.http.HttpClient; import io.bitsquare.http.HttpClient;
import io.bitsquare.http.HttpException; import io.bitsquare.http.HttpException;
@ -63,7 +62,7 @@ public class PoloniexPriceProvider implements PriceProvider {
@Override @Override
public MarketPrice getPrice(String currencyCode) throws IOException, HttpException { public MarketPrice getPrice(String currencyCode) throws IOException, HttpException {
Log.traceCall("currencyCode=" + currencyCode); // Log.traceCall("currencyCode=" + currencyCode);
JsonObject jsonObject = new JsonParser() JsonObject jsonObject = new JsonParser()
.parse(httpClient.requestWithGET(currencyCode)) .parse(httpClient.requestWithGET(currencyCode))
.getAsJsonObject(); .getAsJsonObject();

View file

@ -80,6 +80,10 @@ public class CountryUtil {
return new Locale(LanguageUtil.getDefaultLanguage(), countryCode).getDisplayCountry(); return new Locale(LanguageUtil.getDefaultLanguage(), countryCode).getDisplayCountry();
} }
public static String getNameAndCode(String countryCode) {
return getNameByCode(countryCode) + " (" + countryCode + ")";
}
public static String getCodesString(List<String> countryCodes) { public static String getCodesString(List<String> countryCodes) {
return countryCodes.stream().collect(Collectors.joining(", ")); return countryCodes.stream().collect(Collectors.joining(", "));
} }

View file

@ -171,12 +171,6 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("ETH", "Ethereum")); result.add(new CryptoCurrency("ETH", "Ethereum"));
result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("LTC", "Litecoin"));
result.add(new CryptoCurrency("NMC", "Namecoin")); 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("DASH", "Dash"));
result.add(new CryptoCurrency("NBT", "NuBits")); result.add(new CryptoCurrency("NBT", "NuBits"));
result.add(new CryptoCurrency("NSR", "NuShares")); result.add(new CryptoCurrency("NSR", "NuShares"));
@ -192,6 +186,13 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("BTS", "BitShares")); result.add(new CryptoCurrency("BTS", "BitShares"));
result.add(new CryptoCurrency("XCP", "Counterparty")); result.add(new CryptoCurrency("XCP", "Counterparty"));
result.add(new CryptoCurrency("XRP", "Ripple")); 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; return result;
} }
@ -214,7 +215,7 @@ public class CurrencyUtil {
return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale()); return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale());
} catch (Throwable t) { } catch (Throwable t) {
// Seems that it is a cryptocurrency // Seems that it is a cryptocurrency
return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getCodeAndName(); return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getName();
} }
} }

View file

@ -58,6 +58,10 @@ public class TradeCurrency implements Serializable {
return symbol; return symbol;
} }
public String getNameAndCode() {
return name + " (" + code + ")";
}
public String getCodeAndName() { public String getCodeAndName() {
return code + " (" + name + ")"; return code + " (" + name + ")";
} }

View file

@ -44,6 +44,11 @@ public class AliPayAccountContractData extends PaymentAccountContractData implem
return "AliPay - Account nr.: " + accountNr; return "AliPay - Account nr.: " + accountNr;
} }
@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
}
@Override @Override
public String toString() { public String toString() {
return "AliPayAccountContractData{" + return "AliPayAccountContractData{" +

View file

@ -43,7 +43,12 @@ public class BlockChainAccountContractData extends PaymentAccountContractData im
@Override @Override
public String getPaymentDetails() { public String getPaymentDetails() {
return "Address: " + address; return "Receivers cryptocurrency address: " + address;
}
@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
} }
public void setPaymentId(String paymentId) { public void setPaymentId(String paymentId) {

View file

@ -44,5 +44,8 @@ public class OKPayAccountContractData extends PaymentAccountContractData impleme
return "OKPay - Account nr.: " + accountNr; return "OKPay - Account nr.: " + accountNr;
} }
@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
}
} }

View file

@ -72,6 +72,8 @@ public abstract class PaymentAccountContractData implements Serializable {
abstract public String getPaymentDetails(); abstract public String getPaymentDetails();
abstract public String getPaymentDetailsForTradePopup();
public int getMaxTradePeriod() { public int getMaxTradePeriod() {
return maxTradePeriod; return maxTradePeriod;
} }

View file

@ -44,4 +44,9 @@ public class PerfectMoneyAccountContractData extends PaymentAccountContractData
return "PerfectMoney - Account nr.: " + accountNr; return "PerfectMoney - Account nr.: " + accountNr;
} }
@Override
public String getPaymentDetailsForTradePopup() {
return getPaymentDetails();
}
} }

View file

@ -89,4 +89,12 @@ public class SepaAccountContractData extends PaymentAccountContractData implemen
public String getPaymentDetails() { public String getPaymentDetails() {
return "SEPA - Holder name: " + holderName + ", IBAN: " + iban + ", BIC: " + bic + ", country code: " + getCountryCode(); 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());
}
} }

View file

@ -53,4 +53,9 @@ public class SwishAccountContractData extends PaymentAccountContractData impleme
return "Swish - Holder name: " + holderName + ", mobile nr.: " + mobileNr; return "Swish - Holder name: " + holderName + ", mobile nr.: " + mobileNr;
} }
@Override
public String getPaymentDetailsForTradePopup() {
return "Holder name: " + holderName + "\n" +
"Mobile nr.: " + mobileNr;
}
} }

View file

@ -146,7 +146,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
// Mutable // Mutable
private DecryptedMsgWithPubKey decryptedMsgWithPubKey; 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 int takeOfferDateAsBlockHeight;
private Coin tradeAmount; private Coin tradeAmount;
private NodeAddress tradingPeerNodeAddress; private NodeAddress tradingPeerNodeAddress;
@ -180,6 +180,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
protected Trade(Offer offer, Storage<? extends TradableList> storage) { protected Trade(Offer offer, Storage<? extends TradableList> storage) {
this.offer = offer; this.offer = offer;
this.storage = storage; this.storage = storage;
this.takeOfferDate = new Date();
processModel = new ProcessModel(); processModel = new ProcessModel();
tradeVolumeProperty = new SimpleObjectProperty<>(); tradeVolumeProperty = new SimpleObjectProperty<>();
@ -199,6 +200,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
this.tradingPeerNodeAddress = tradingPeerNodeAddress; this.tradingPeerNodeAddress = tradingPeerNodeAddress;
tradeAmountProperty.set(tradeAmount); tradeAmountProperty.set(tradeAmount);
tradeVolumeProperty.set(getTradeVolume()); tradeVolumeProperty.set(getTradeVolume());
this.takeOfferDate = new Date();
} }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@ -411,9 +413,9 @@ abstract public class Trade implements Tradable, Model, Serializable {
return takeOfferDate; return takeOfferDate;
} }
public void setTakeOfferDate(Date takeOfferDate) { /*public void setTakeOfferDate(Date takeOfferDate) {
this.takeOfferDate = takeOfferDate; this.takeOfferDate = takeOfferDate;
} }*/
public int getTakeOfferDateAsBlockHeight() { public int getTakeOfferDateAsBlockHeight() {
return takeOfferDateAsBlockHeight; return takeOfferDateAsBlockHeight;

View file

@ -59,7 +59,6 @@ import org.spongycastle.crypto.params.KeyParameter;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import java.io.File; import java.io.File;
import java.util.Date;
import java.util.Optional; import java.util.Optional;
import static io.bitsquare.util.Validator.nonEmptyStringOf; import static io.bitsquare.util.Validator.nonEmptyStringOf;
@ -98,7 +97,6 @@ public class TradeManager {
ArbitratorManager arbitratorManager, ArbitratorManager arbitratorManager,
P2PService p2PService, P2PService p2PService,
@Named("storage.dir") File storageDir) { @Named("storage.dir") File storageDir) {
Log.traceCall();
this.user = user; this.user = user;
this.keyRing = keyRing; this.keyRing = keyRing;
this.walletService = walletService; this.walletService = walletService;
@ -280,7 +278,7 @@ public class TradeManager {
else else
trade = new BuyerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage); trade = new BuyerAsTakerTrade(offer, amount, model.getPeerNodeAddress(), tradableListStorage);
trade.setTakeOfferDate(new Date()); //trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(tradeWalletService.getBestChainHeight()); trade.setTakeOfferDateAsBlockHeight(tradeWalletService.getBestChainHeight());
trade.setTakerPaymentAccountId(paymentAccountId); trade.setTakerPaymentAccountId(paymentAccountId);
@ -312,8 +310,8 @@ public class TradeManager {
public void onSuccess(@javax.annotation.Nullable Transaction transaction) { public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
if (transaction != null) { if (transaction != null) {
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString()); log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
trade.setState(Trade.State.WITHDRAW_COMPLETED);
addTradeToClosedTrades(trade); addTradeToClosedTrades(trade);
trade.setState(Trade.State.WITHDRAW_COMPLETED);
resultHandler.handleResult(); resultHandler.handleResult();
} }
} }

View file

@ -25,6 +25,7 @@ import io.bitsquare.trade.TradableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -55,6 +56,18 @@ public class OpenOffer implements Tradable, Serializable {
this.storage = storage; 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() { public Date getDate() {
return offer.getDate(); return offer.getDate();
} }
@ -98,7 +111,7 @@ public class OpenOffer implements Tradable, Serializable {
stopTimeout(); stopTimeout();
timeoutTimer = UserThread.runAfter(() -> { timeoutTimer = UserThread.runAfter(() -> {
log.info("Timeout reached"); log.info("Timeout for resettin State.RESERVED reached");
if (state == State.RESERVED) if (state == State.RESERVED)
setState(State.AVAILABLE); setState(State.AVAILABLE);
}, TIMEOUT_SEC); }, TIMEOUT_SEC);

View file

@ -47,13 +47,13 @@ public class ProcessFinalizePayoutTxRequest extends TradeTask {
processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddress)); processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddress));
trade.setLockTimeAsBlockHeight(nonNegativeLongOf(message.lockTimeAsBlockHeight)); 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 // update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing(); removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.FIAT_PAYMENT_RECEIPT_MSG_RECEIVED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View file

@ -27,8 +27,6 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Date;
public class SignAndPublishDepositTxAsBuyer extends TradeTask { public class SignAndPublishDepositTxAsBuyer extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsBuyer.class); private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsBuyer.class);
@ -64,7 +62,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
log.trace("takerSignAndPublishTx succeeded " + transaction); log.trace("takerSignAndPublishTx succeeded " + transaction);
trade.setDepositTx(transaction); trade.setDepositTx(transaction);
trade.setTakeOfferDate(new Date()); //trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
trade.setState(Trade.State.DEPOSIT_PUBLISHED); trade.setState(Trade.State.DEPOSIT_PUBLISHED);

View file

@ -26,8 +26,6 @@ import org.bitcoinj.core.Transaction;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Date;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static io.bitsquare.util.Validator.checkTradeId; import static io.bitsquare.util.Validator.checkTradeId;
@ -54,8 +52,7 @@ public class ProcessDepositTxPublishedMessage extends TradeTask {
// update with full tx // update with full tx
trade.setDepositTx(processModel.getTradeWalletService().addTransactionToWallet(transactionFromSerializedTx)); trade.setDepositTx(processModel.getTradeWalletService().addTransactionToWallet(transactionFromSerializedTx));
trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED); //trade.setTakeOfferDate(new Date());
trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
if (trade instanceof OffererTrade) if (trade instanceof OffererTrade)
@ -66,6 +63,8 @@ public class ProcessDepositTxPublishedMessage extends TradeTask {
removeMailboxMessageAfterProcessing(); removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View file

@ -36,7 +36,6 @@ public class SendPublishDepositTxRequest extends TradeTask {
protected void run() { protected void run() {
try { try {
runInterceptHook(); runInterceptHook();
trade.setState(Trade.State.DEPOSIT_PUBLISH_REQUESTED);
PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest( PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest(
processModel.getId(), processModel.getId(),
processModel.getPaymentAccountContractData(trade), 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) { } catch (Throwable t) {
failed(t); failed(t);
} }

View file

@ -46,13 +46,13 @@ public class ProcessFiatTransferStartedMessage extends TradeTask {
processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.buyerPayoutAddress)); 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 // update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing(); removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.FIAT_PAYMENT_STARTED_MSG_RECEIVED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View file

@ -46,13 +46,13 @@ public class ProcessPayoutTxFinalizedMessage extends TradeTask {
checkArgument(message.payoutTx != null); checkArgument(message.payoutTx != null);
trade.setPayoutTx(processModel.getWalletService().getTransactionFromSerializedTx(message.payoutTx)); 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 // update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress()); trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing(); removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.PAYOUT_TX_RECEIVED);
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View file

@ -27,8 +27,6 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Date;
public class SignAndPublishDepositTxAsSeller extends TradeTask { public class SignAndPublishDepositTxAsSeller extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsSeller.class); private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsSeller.class);
@ -62,7 +60,7 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask {
log.trace("takerSignAndPublishTx succeeded " + transaction); log.trace("takerSignAndPublishTx succeeded " + transaction);
trade.setDepositTx(transaction); trade.setDepositTx(transaction);
trade.setTakeOfferDate(new Date()); //trade.setTakeOfferDate(new Date());
trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight()); trade.setTakeOfferDateAsBlockHeight(processModel.getTradeWalletService().getBestChainHeight());
trade.setState(Trade.State.DEPOSIT_PUBLISHED); trade.setState(Trade.State.DEPOSIT_PUBLISHED);

View file

@ -634,49 +634,6 @@ textfield */
-fx-base: #dd0000; -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 */ /* TitledGroupBg */
#titled-group-bg-label { #titled-group-bg-label {
-fx-font-weight: bold; -fx-font-weight: bold;

View file

@ -93,6 +93,10 @@ public class TextFieldWithCopyIcon extends AnchorPane {
this.text.set(text); this.text.set(text);
} }
public void setTooltip(Tooltip toolTip) {
textField.setTooltip(toolTip);
}
public void setCopyWithoutCurrencyPostFix(boolean copyWithoutCurrencyPostFix) { public void setCopyWithoutCurrencyPostFix(boolean copyWithoutCurrencyPostFix) {
this.copyWithoutCurrencyPostFix = copyWithoutCurrencyPostFix; this.copyWithoutCurrencyPostFix = copyWithoutCurrencyPostFix;
} }

View file

@ -75,12 +75,15 @@ public class TxIdTextField extends AnchorPane {
copyIcon = new Label(); copyIcon = new Label();
copyIcon.setLayoutY(3); copyIcon.setLayoutY(3);
copyIcon.getStyleClass().add("copy-icon"); 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); AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
AnchorPane.setRightAnchor(copyIcon, 30.0); AnchorPane.setRightAnchor(copyIcon, 30.0);
Tooltip tooltip = new Tooltip("Open a blockchain explorer with that transactions ID");
blockExplorerIcon = new Label(); blockExplorerIcon = new Label();
blockExplorerIcon.getStyleClass().add("external-link-icon"); blockExplorerIcon.getStyleClass().add("external-link-icon");
blockExplorerIcon.setTooltip(tooltip);
AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK); AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK);
blockExplorerIcon.setMinWidth(20); blockExplorerIcon.setMinWidth(20);
AnchorPane.setRightAnchor(blockExplorerIcon, 52.0); AnchorPane.setRightAnchor(blockExplorerIcon, 52.0);
@ -89,7 +92,7 @@ public class TxIdTextField extends AnchorPane {
textField = new TextField(); textField = new TextField();
textField.setId("address-text-field"); textField.setId("address-text-field");
textField.setEditable(false); 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.setRightAnchor(textField, 80.0);
AnchorPane.setLeftAnchor(textField, 0.0); AnchorPane.setLeftAnchor(textField, 0.0);
textField.focusTraversableProperty().set(focusTraversableProperty().get()); textField.focusTraversableProperty().set(focusTraversableProperty().get());

View file

@ -63,7 +63,7 @@ public class AliPayForm extends PaymentMethodForm {
updateFromInputs(); updateFromInputs();
}); });
addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
addAccountNameTextFieldWithAutoFillCheckBox(); addAccountNameTextFieldWithAutoFillCheckBox();
} }
@ -85,7 +85,7 @@ public class AliPayForm extends PaymentMethodForm {
addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(aliPayAccount.getPaymentMethod().getId())); addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(aliPayAccount.getPaymentMethod().getId()));
TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", aliPayAccount.getAccountNr()).second; TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", aliPayAccount.getAccountNr()).second;
field.setMouseTransparent(false); field.setMouseTransparent(false);
addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", aliPayAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
} }

View file

@ -49,7 +49,7 @@ public class BlockChainForm extends PaymentMethodForm {
private ComboBox<TradeCurrency> currencyComboBox; private ComboBox<TradeCurrency> currencyComboBox;
public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { 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 && if (paymentAccountContractData instanceof BlockChainAccountContractData &&
((BlockChainAccountContractData) paymentAccountContractData).getPaymentId() != null) ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId() != null)
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Payment ID:", ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId()); addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Payment ID:", ((BlockChainAccountContractData) paymentAccountContractData).getPaymentId());
@ -70,7 +70,7 @@ public class BlockChainForm extends PaymentMethodForm {
addTradeCurrencyComboBox(); addTradeCurrencyComboBox();
currencyComboBox.setPrefWidth(250); currencyComboBox.setPrefWidth(250);
addressInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Receiving altcoin address:").second; addressInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Cryptocurrency address:").second;
addressInputTextField.setValidator(altCoinAddressValidator); addressInputTextField.setValidator(altCoinAddressValidator);
addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> { addressInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
@ -98,9 +98,9 @@ public class BlockChainForm extends PaymentMethodForm {
gridRowFrom = gridRow; gridRowFrom = gridRow;
addLabelTextField(gridPane, gridRow, "Account name:", blockChainAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, gridRow, "Account name:", blockChainAccount.getAccountName(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(blockChainAccount.getPaymentMethod().getId())); 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); field.setMouseTransparent(false);
addLabelTextField(gridPane, ++gridRow, "Crypto currency:", blockChainAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Crypto currency:", blockChainAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
} }
@ -120,7 +120,7 @@ public class BlockChainForm extends PaymentMethodForm {
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() { currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override

View file

@ -65,7 +65,7 @@ public abstract class PaymentMethodForm {
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() { currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override
@ -111,7 +111,7 @@ public abstract class PaymentMethodForm {
displayText = hours / 24 + " days"; 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() { protected void addAllowedPeriod() {
@ -126,7 +126,7 @@ public abstract class PaymentMethodForm {
displayText += " (Max. permitted period until the trade has to be completed)"; 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(); abstract protected void autoFillNameTextField();

View file

@ -64,7 +64,7 @@ public class PerfectMoneyForm extends PaymentMethodForm {
updateFromInputs(); updateFromInputs();
}); });
addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
addAccountNameTextFieldWithAutoFillCheckBox(); addAccountNameTextFieldWithAutoFillCheckBox();
} }
@ -87,7 +87,7 @@ public class PerfectMoneyForm extends PaymentMethodForm {
addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(perfectMoneyAccount.getPaymentMethod().getId())); addLabelTextField(gridPane, ++gridRow, "Payment method:", BSResources.get(perfectMoneyAccount.getPaymentMethod().getId()));
TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", perfectMoneyAccount.getAccountNr()).second; TextField field = addLabelTextField(gridPane, ++gridRow, "Account nr.:", perfectMoneyAccount.getAccountNr()).second;
field.setMouseTransparent(false); field.setMouseTransparent(false);
addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", perfectMoneyAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
} }

View file

@ -59,7 +59,7 @@ public class SepaForm extends PaymentMethodForm {
public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) { public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) {
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "Account holder name:", ((SepaAccountContractData) paymentAccountContractData).getHolderName()); 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, "IBAN:", ((SepaAccountContractData) paymentAccountContractData).getIban());
addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "BIC/SWIFT:", ((SepaAccountContractData) paymentAccountContractData).getBic()); addLabelTextFieldWithCopyIcon(gridPane, ++gridRow, "BIC/SWIFT:", ((SepaAccountContractData) paymentAccountContractData).getBic());
return gridRow; return gridRow;
@ -107,7 +107,7 @@ public class SepaForm extends PaymentMethodForm {
countryComboBox.setConverter(new StringConverter<Country>() { countryComboBox.setConverter(new StringConverter<Country>() {
@Override @Override
public String toString(Country country) { public String toString(Country country) {
return country.code + " (" + country.name + ")"; return country.name + " (" + country.code + ")";
} }
@Override @Override
@ -120,7 +120,7 @@ public class SepaForm extends PaymentMethodForm {
sepaAccount.setCountry(selectedItem); sepaAccount.setCountry(selectedItem);
TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(selectedItem.code); TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(selectedItem.code);
sepaAccount.setSingleTradeCurrency(currency); sepaAccount.setSingleTradeCurrency(currency);
currencyTextField.setText("Currency: " + currency.getCodeAndName()); currencyTextField.setText("Currency: " + currency.getNameAndCode());
updateCountriesSelection(true, euroCountryCheckBoxes); updateCountriesSelection(true, euroCountryCheckBoxes);
updateCountriesSelection(true, nonEuroCountryCheckBoxes); updateCountriesSelection(true, nonEuroCountryCheckBoxes);
updateFromInputs(); updateFromInputs();
@ -138,7 +138,7 @@ public class SepaForm extends PaymentMethodForm {
sepaAccount.setCountry(country); sepaAccount.setCountry(country);
TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(country.code); TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(country.code);
sepaAccount.setSingleTradeCurrency(currency); sepaAccount.setSingleTradeCurrency(currency);
currencyTextField.setText("Currency: " + currency.getCodeAndName()); currencyTextField.setText("Currency: " + currency.getNameAndCode());
} }
updateFromInputs(); updateFromInputs();
@ -256,7 +256,7 @@ public class SepaForm extends PaymentMethodForm {
TextField bicField = addLabelTextField(gridPane, ++gridRow, "BIC/SWIFT:", sepaAccount.getBic()).second; TextField bicField = addLabelTextField(gridPane, ++gridRow, "BIC/SWIFT:", sepaAccount.getBic()).second;
bicField.setMouseTransparent(false); bicField.setMouseTransparent(false);
addLabelTextField(gridPane, ++gridRow, "Location of Bank:", sepaAccount.getCountry().name); 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; String countries;
Tooltip tooltip = null; Tooltip tooltip = null;
if (CountryUtil.containsAllSepaEuroCountries(sepaAccount.getAcceptedCountryCodes())) { if (CountryUtil.containsAllSepaEuroCountries(sepaAccount.getAcceptedCountryCodes())) {

View file

@ -72,7 +72,7 @@ public class SwishForm extends PaymentMethodForm {
updateFromInputs(); updateFromInputs();
}); });
addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
addAccountNameTextFieldWithAutoFillCheckBox(); addAccountNameTextFieldWithAutoFillCheckBox();
} }
@ -95,7 +95,7 @@ public class SwishForm extends PaymentMethodForm {
addLabelTextField(gridPane, ++gridRow, "Account holder name:", swishAccount.getHolderName()); addLabelTextField(gridPane, ++gridRow, "Account holder name:", swishAccount.getHolderName());
TextField field = addLabelTextField(gridPane, ++gridRow, "Mobile nr.:", swishAccount.getMobileNr()).second; TextField field = addLabelTextField(gridPane, ++gridRow, "Mobile nr.:", swishAccount.getMobileNr()).second;
field.setMouseTransparent(false); field.setMouseTransparent(false);
addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getCodeAndName()); addLabelTextField(gridPane, ++gridRow, "Currency:", swishAccount.getSingleTradeCurrency().getNameAndCode());
addAllowedPeriod(); addAllowedPeriod();
} }

View file

@ -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.buyer.*;
import io.bitsquare.trade.protocol.trade.tasks.offerer.*; import io.bitsquare.trade.protocol.trade.tasks.offerer.*;
import io.bitsquare.trade.protocol.trade.tasks.seller.*; 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.CommitPayoutTx;
import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute; 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 io.bitsquare.trade.protocol.trade.tasks.taker.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -99,7 +99,7 @@ public class DebugView extends InitializableView {
SignAndFinalizePayoutTx.class, SignAndFinalizePayoutTx.class,
CommitPayoutTx.class, CommitPayoutTx.class,
SendPayoutTxFinalizedMessage.class, SendPayoutTxFinalizedMessage.class,
SetupPayoutTxLockTimeReachedListener.class, BroadcastAfterLockTime.class,
Boolean.class, /* used as seperator*/ Boolean.class, /* used as seperator*/
@ -125,7 +125,7 @@ public class DebugView extends InitializableView {
ProcessPayoutTxFinalizedMessage.class, ProcessPayoutTxFinalizedMessage.class,
CommitPayoutTx.class, CommitPayoutTx.class,
SetupPayoutTxLockTimeReachedListener.class, BroadcastAfterLockTime.class,
Boolean.class /* used as seperator*/ Boolean.class /* used as seperator*/
) )
); );
@ -152,7 +152,7 @@ public class DebugView extends InitializableView {
SignAndFinalizePayoutTx.class, SignAndFinalizePayoutTx.class,
CommitPayoutTx.class, CommitPayoutTx.class,
SendPayoutTxFinalizedMessage.class, SendPayoutTxFinalizedMessage.class,
SetupPayoutTxLockTimeReachedListener.class, BroadcastAfterLockTime.class,
Boolean.class, /* used as seperator*/ Boolean.class, /* used as seperator*/
@ -177,7 +177,7 @@ public class DebugView extends InitializableView {
ProcessPayoutTxFinalizedMessage.class, ProcessPayoutTxFinalizedMessage.class,
CommitPayoutTx.class, CommitPayoutTx.class,
SetupPayoutTxLockTimeReachedListener.class, BroadcastAfterLockTime.class,
Boolean.class /* used as seperator*/ Boolean.class /* used as seperator*/
) )
); );

View file

@ -86,7 +86,7 @@ public class MarketsChartsView extends ActivatableViewAndModel<VBox, MarketsChar
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() { currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override

View file

@ -289,7 +289,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.onPaymentAccountSelected(paymentAccount); model.onPaymentAccountSelected(paymentAccount);
} else { } else {
currencyTextField.setText(paymentAccount.getSingleTradeCurrency().getCodeAndName()); currencyTextField.setText(paymentAccount.getSingleTradeCurrency().getNameAndCode());
model.onPaymentAccountSelected(paymentAccount); model.onPaymentAccountSelected(paymentAccount);
model.onCurrencySelected(paymentAccount.getSingleTradeCurrency()); model.onCurrencySelected(paymentAccount.getSingleTradeCurrency());
} }
@ -588,7 +588,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() { currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override
@ -673,7 +673,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
balanceTextField = balanceTuple.second; balanceTextField = balanceTuple.second;
balanceTextField.setVisible(false); balanceTextField.setVisible(false);
Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow,
BSResources.get("createOffer.fundsBox.placeOffer")); BSResources.get("createOffer.fundsBox.placeOffer"));
placeOfferButton = placeOfferTuple.first; placeOfferButton = placeOfferTuple.first;
placeOfferButton.setVisible(false); placeOfferButton.setVisible(false);

View file

@ -89,7 +89,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() { currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override
@ -160,6 +160,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem())); currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()));
paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem()));
createOfferButton.setOnAction(e -> onCreateOffer()); createOfferButton.setOnAction(e -> onCreateOffer());
priceColumn.textProperty().bind(createStringBinding(
() -> "Price in " + model.tradeCurrencyCode.get() + "/BTC", model.tradeCurrencyCode));
volumeColumn.textProperty().bind(createStringBinding( volumeColumn.textProperty().bind(createStringBinding(
() -> "Amount in " + model.tradeCurrencyCode.get() + " (Min.)", model.tradeCurrencyCode)); () -> "Amount in " + model.tradeCurrencyCode.get() + " (Min.)", model.tradeCurrencyCode));
model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty()); model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty());

View file

@ -317,7 +317,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
paymentMethodLabel.setManaged(!showComboBox); paymentMethodLabel.setManaged(!showComboBox);
if (!showComboBox) if (!showComboBox)
paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId())); paymentMethodTextField.setText(BSResources.get(model.getPaymentMethod().getId()));
currencyTextField.setText(model.getTradeCurrency().getCodeAndName()); currencyTextField.setText(model.getTradeCurrency().getNameAndCode());
buyLabel.setText(model.getDirectionLabel()); buyLabel.setText(model.getDirectionLabel());
amountDescriptionLabel.setText(model.getAmountDescription()); amountDescriptionLabel.setText(model.getAmountDescription());
amountRangeTextField.setText(model.getAmountRange()); amountRangeTextField.setText(model.getAmountRange());
@ -553,7 +553,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
balanceTextField = balanceTuple.second; balanceTextField = balanceTuple.second;
balanceTextField.setVisible(false); balanceTextField.setVisible(false);
Tuple3<Button, ProgressIndicator, Label> takeOfferTuple = addButtonWithStatus(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.takeOffer")); Tuple3<Button, ProgressIndicator, Label> takeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, BSResources.get("takeOffer.fundsBox.takeOffer"));
takeOfferButton = takeOfferTuple.first; takeOfferButton = takeOfferTuple.first;
takeOfferButton.setVisible(false); takeOfferButton.setVisible(false);
takeOfferButton.setOnAction(e -> onTakeOffer()); takeOfferButton.setOnAction(e -> onTakeOffer());

View file

@ -87,7 +87,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
tradeCurrencyComboBox.setConverter(new StringConverter<TradeCurrency>() { tradeCurrencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getCodeAndName(); return tradeCurrency.getNameAndCode();
} }
@Override @Override

View file

@ -148,7 +148,8 @@ public class OfferDetailsPopup extends Popup {
addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId())); addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
rows = 3; rows = 3;
if (offer.getPaymentMethodCountryCode() != null) String paymentMethodCountryCode = offer.getPaymentMethodCountryCode();
if (paymentMethodCountryCode != null)
rows++; rows++;
if (offer.getOfferFeePaymentTxID() != null) if (offer.getOfferFeePaymentTxID() != null)
rows++; 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, "Offer ID:", offer.getId(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
addLabelTextField(gridPane, ++rowIndex, "Creation date:", formatter.formatDateTime(offer.getDate())); addLabelTextField(gridPane, ++rowIndex, "Creation date:", formatter.formatDateTime(offer.getDate()));
if (offer.getPaymentMethodCountryCode() != null) if (paymentMethodCountryCode != null)
addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:", offer.getPaymentMethodCountryCode()); addLabelTextField(gridPane, ++rowIndex, "Offerers country of bank:",
CountryUtil.getNameAndCode(paymentMethodCountryCode));
if (offer.getAcceptedCountryCodes() != null) { if (offer.getAcceptedCountryCodes() != null) {
String countries; String countries;
Tooltip tooltip = null; Tooltip tooltip = null;

View file

@ -129,6 +129,7 @@ public class FormBuilder {
return label; return label;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Label + TextField // Label + TextField
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -672,7 +673,7 @@ public class FormBuilder {
// Button + ProgressIndicator + Label // Button + ProgressIndicator + Label
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public static Tuple3<Button, ProgressIndicator, Label> addButtonWithStatus(GridPane gridPane, public static Tuple3<Button, ProgressIndicator, Label> addButtonWithStatusAfterGroup(GridPane gridPane,
int rowIndex, int rowIndex,
String buttonTitle) { String buttonTitle) {
return addButtonWithStatus(gridPane, rowIndex, buttonTitle, 15); return addButtonWithStatus(gridPane, rowIndex, buttonTitle, 15);

View file

@ -20,6 +20,7 @@ public class HttpClient implements Serializable {
connection = (HttpURLConnection) url.openConnection(); connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
connection.setConnectTimeout(10000); connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
if (connection.getResponseCode() == 200) { if (connection.getResponseCode() == 200) {
return convertInputStreamToString(connection.getInputStream()); return convertInputStreamToString(connection.getInputStream());

View file

@ -16,10 +16,7 @@ import io.bitsquare.crypto.EncryptionService;
import io.bitsquare.crypto.PrefixedSealedAndSignedMessage; import io.bitsquare.crypto.PrefixedSealedAndSignedMessage;
import io.bitsquare.p2p.messaging.*; import io.bitsquare.p2p.messaging.*;
import io.bitsquare.p2p.network.*; import io.bitsquare.p2p.network.*;
import io.bitsquare.p2p.peers.Broadcaster; import io.bitsquare.p2p.peers.*;
import io.bitsquare.p2p.peers.PeerExchangeManager;
import io.bitsquare.p2p.peers.PeerManager;
import io.bitsquare.p2p.peers.RequestDataManager;
import io.bitsquare.p2p.seed.SeedNodesRepository; import io.bitsquare.p2p.seed.SeedNodesRepository;
import io.bitsquare.p2p.storage.HashMapChangedListener; import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.P2PDataStorage; import io.bitsquare.p2p.storage.P2PDataStorage;
@ -81,6 +78,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private Subscription networkReadySubscription; private Subscription networkReadySubscription;
private boolean isBootstrapped; private boolean isBootstrapped;
private ChangeListener<Number> numOfBroadcastsChangeListener; private ChangeListener<Number> numOfBroadcastsChangeListener;
private MaintenanceManager maintenanceManager;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -128,6 +126,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
peerExchangeManager = new PeerExchangeManager(networkNode, peerManager, seedNodeAddresses); 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 // We need to have both the initial data delivered and the hidden service published
networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived, networkReadyBinding = EasyBind.combine(hiddenServicePublished, preliminaryDataReceived,
(hiddenServicePublished, preliminaryDataReceived) (hiddenServicePublished, preliminaryDataReceived)
@ -170,6 +171,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
if (peerExchangeManager != null) if (peerExchangeManager != null)
peerExchangeManager.shutDown(); peerExchangeManager.shutDown();
if (maintenanceManager != null)
maintenanceManager.shutDown();
if (networkNode != null) if (networkNode != null)
networkNode.shutDown(() -> { networkNode.shutDown(() -> {
shutDownResultHandlers.stream().forEach(Runnable::run); shutDownResultHandlers.stream().forEach(Runnable::run);
@ -222,6 +226,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
hiddenServicePublished.set(true); hiddenServicePublished.set(true);
p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished); p2pServiceListeners.stream().forEach(SetupListener::onHiddenServicePublished);
maintenanceManager.start();
} }
@Override @Override

View file

@ -62,7 +62,7 @@ public class Connection implements MessageListener {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private final Socket socket; private final Socket socket;
private final MessageListener messageListener; // private final MessageListener messageListener;
private final ConnectionListener connectionListener; private final ConnectionListener connectionListener;
private final String portInfo; private final String portInfo;
private final String uid = UUID.randomUUID().toString(); private final String uid = UUID.randomUUID().toString();
@ -85,6 +85,8 @@ public class Connection implements MessageListener {
private PeerType peerType; private PeerType peerType;
private final ObjectProperty<NodeAddress> nodeAddressProperty = new SimpleObjectProperty<>(); private final ObjectProperty<NodeAddress> nodeAddressProperty = new SimpleObjectProperty<>();
private List<Long> messageTimeStamps = new ArrayList<>(); private List<Long> messageTimeStamps = new ArrayList<>();
private final CopyOnWriteArraySet<MessageListener> messageListeners = new CopyOnWriteArraySet<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -93,9 +95,11 @@ public class Connection implements MessageListener {
Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener, Connection(Socket socket, MessageListener messageListener, ConnectionListener connectionListener,
@Nullable NodeAddress peersNodeAddress) { @Nullable NodeAddress peersNodeAddress) {
this.socket = socket; this.socket = socket;
this.messageListener = messageListener; //this.messageListener = messageListener;
this.connectionListener = connectionListener; this.connectionListener = connectionListener;
addMessageListener(messageListener);
sharedModel = new SharedModel(this, socket); sharedModel = new SharedModel(this, socket);
if (socket.getLocalPort() == 0) if (socket.getLocalPort() == 0)
@ -143,7 +147,6 @@ public class Connection implements MessageListener {
// API // API
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Called form various threads // Called form various threads
public void sendMessage(Message message) { public void sendMessage(Message message) {
if (!stopped) { 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") @SuppressWarnings("unused")
public void reportIllegalRequest(RuleViolation ruleViolation) { public void reportIllegalRequest(RuleViolation ruleViolation) {
sharedModel.reportInvalidRequest(ruleViolation); sharedModel.reportInvalidRequest(ruleViolation);
@ -224,11 +240,11 @@ public class Connection implements MessageListener {
// MessageListener implementation // MessageListener implementation
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Only get non - CloseConnectionMessage messages // Only receive non - CloseConnectionMessage messages
@Override @Override
public void onMessage(Message message, Connection connection) { public void onMessage(Message message, Connection connection) {
// connection is null as we get called from InputHandler, which does not hold a reference to Connection checkArgument(connection.equals(this));
UserThread.execute(() -> messageListener.onMessage(message, this)); UserThread.execute(() -> messageListeners.stream().forEach(e -> e.onMessage(message, connection)));
} }

View file

@ -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<Connection> future = networkNode.sendMessage(connection,
getPeersResponse);
Futures.addCallback(future, new FutureCallback<Connection>() {
@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;
}
}
}

View file

@ -118,10 +118,7 @@ public class MaintenanceHandshake implements MessageListener {
HashSet<ReportedPeer> reportedPeers = getPeersRequest.reportedPeers; HashSet<ReportedPeer> reportedPeers = getPeersRequest.reportedPeers;
/* StringBuilder result = new StringBuilder("Received peers:"); peerManager.printReportedPeers(reportedPeers);
reportedPeers.stream().forEach(e -> result.append("\n\t").append(e));
log.trace(result.toString());*/
log.trace("reportedPeers.size=" + reportedPeers.size());
checkArgument(connection.getPeersNodeAddressOptional().isPresent(), checkArgument(connection.getPeersNodeAddressOptional().isPresent(),
"The peers address must have been already set at the moment"); "The peers address must have been already set at the moment");

View file

@ -7,7 +7,6 @@ import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.*; import io.bitsquare.p2p.network.*;
import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -18,16 +17,17 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class MaintenanceManager implements MessageListener, ConnectionListener { public class MaintenanceManager implements MessageListener, ConnectionListener {
private static final Logger log = LoggerFactory.getLogger(MaintenanceManager.class); private static final Logger log = LoggerFactory.getLogger(MaintenanceManager.class);
private static final int MAINTENANCE_DELAY_SEC = 5 * 60;
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final PeerManager peerManager; private final PeerManager peerManager;
private final Set<NodeAddress> seedNodeAddresses; private final Set<NodeAddress> seedNodeAddresses;
private final ScheduledThreadPoolExecutor executor; private ScheduledThreadPoolExecutor executor;
private final Map<NodeAddress, PeerExchangeHandshake> peerExchangeHandshakeMap = new HashMap<>(); private final Map<NodeAddress, PeerExchangeHandler> peerExchangeHandshakeMap = new HashMap<>();
private Timer connectToMorePeersTimer, maintainConnectionsTimer; private Timer connectToMorePeersTimer, maintainConnectionsTimer;
private boolean shutDownInProgress; private boolean shutDownInProgress;
@ -42,7 +42,6 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
checkArgument(!seedNodeAddresses.isEmpty(), "seedNodeAddresses must not be empty"); checkArgument(!seedNodeAddresses.isEmpty(), "seedNodeAddresses must not be empty");
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses); this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
executor = Utilities.getScheduledThreadPoolExecutor("PeerExchangeManager", 1, 10, 5);
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
} }
@ -53,8 +52,10 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
networkNode.removeMessageListener(this); networkNode.removeMessageListener(this);
stopConnectToMorePeersTimer(); stopConnectToMorePeersTimer();
stopMaintainConnectionsTimer(); stopMaintainConnectionsTimer();
peerExchangeHandshakeMap.values().stream().forEach(PeerExchangeHandshake::closeHandshake); peerExchangeHandshakeMap.values().stream().forEach(PeerExchangeHandler::cleanup);
MoreExecutors.shutdownAndAwaitTermination(executor, 500, TimeUnit.MILLISECONDS);
if (executor != null)
MoreExecutors.shutdownAndAwaitTermination(executor, 100, TimeUnit.MILLISECONDS);
} }
@ -62,18 +63,14 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
// API // API
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void requestReportedPeersFromSeedNodes(NodeAddress nodeAddress) { public void start() {
checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at requestReportedPeers"); if (executor == null) {
ArrayList<NodeAddress> remainingNodeAddresses = new ArrayList<>(seedNodeAddresses); executor = Utilities.getScheduledThreadPoolExecutor("MaintenanceManager", 1, 2, 5);
remainingNodeAddresses.remove(nodeAddress); int delay = new Random().nextInt(120) + MAINTENANCE_DELAY_SEC; // add 1-2 min. randomness
Collections.shuffle(remainingNodeAddresses);
requestReportedPeers(nodeAddress, remainingNodeAddresses);
int delay = new Random().nextInt(60) + 60 * 3; // 3-4 min
executor.scheduleAtFixedRate(() -> UserThread.execute(this::maintainConnections), executor.scheduleAtFixedRate(() -> UserThread.execute(this::maintainConnections),
delay, delay, TimeUnit.SECONDS); delay, delay, TimeUnit.SECONDS);
} }
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// ConnectionListener implementation // ConnectionListener implementation
@ -85,13 +82,13 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
@Override @Override
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { 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 // The more connections we have the more relaxed we are with a checkConnections
stopMaintainConnectionsTimer(); stopMaintainConnectionsTimer();
int size = networkNode.getAllConnections().size(); int size = networkNode.getAllConnections().size();
int delay = 10 + 2 * size * size; // 12 sec - 210 sec (3.5 min) int delay = 10 + 2 * size * size; // 12 sec - 210 sec (3.5 min)
maintainConnectionsTimer = UserThread.runAfter(this::maintainConnections, maintainConnectionsTimer = UserThread.runAfter(this::maintainConnections,
delay, TimeUnit.SECONDS); delay, TimeUnit.SECONDS);*/
} }
@Override @Override
@ -105,7 +102,7 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
@Override @Override
public void onMessage(Message message, Connection connection) { public void onMessage(Message message, Connection connection) {
if (message instanceof GetPeersRequest) { /* if (message instanceof GetPeersRequest) {
Log.traceCall(message.toString() + "\n\tconnection=" + connection); Log.traceCall(message.toString() + "\n\tconnection=" + connection);
PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode,
peerManager, peerManager,
@ -123,7 +120,7 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
} }
}); });
peerExchangeHandshake.onGetPeersRequest((GetPeersRequest) message, connection); peerExchangeHandshake.onGetPeersRequest((GetPeersRequest) message, connection);
} }*/
} }
@ -134,9 +131,9 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
private void requestReportedPeers(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) { private void requestReportedPeers(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) {
Log.traceCall("nodeAddress=" + nodeAddress); Log.traceCall("nodeAddress=" + nodeAddress);
if (!peerExchangeHandshakeMap.containsKey(nodeAddress)) { if (!peerExchangeHandshakeMap.containsKey(nodeAddress)) {
PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode,
peerManager, peerManager,
new PeerExchangeHandshake.Listener() { new PeerExchangeHandler.Listener() {
@Override @Override
public void onComplete() { public void onComplete() {
log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress); log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress);
@ -167,8 +164,8 @@ public class MaintenanceManager implements MessageListener, ConnectionListener {
} }
} }
}); });
peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandshake); peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandler);
peerExchangeHandshake.requestConnectedPeers(nodeAddress); peerExchangeHandler.requestConnectedPeers(nodeAddress);
} else { } else {
//TODO check when that happens //TODO check when that happens
log.warn("We have started already a peerExchangeHandshake. " + log.warn("We have started already a peerExchangeHandshake. " +

View file

@ -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<Connection> future = networkNode.sendMessage(nodeAddress, getPeersRequest);
Futures.addCallback(future, new FutureCallback<Connection>() {
@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);
}
}

View file

@ -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<Connection> future = networkNode.sendMessage(nodeAddress, getPeersRequest);
Futures.addCallback(future, new FutureCallback<Connection>() {
@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<ReportedPeer> 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<Connection> future = networkNode.sendMessage(connection,
getPeersResponse);
Futures.addCallback(future, new FutureCallback<Connection>() {
@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<ReportedPeer> 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<ReportedPeer> 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;
}
}
}

View file

@ -1,16 +1,20 @@
package io.bitsquare.p2p.peers; package io.bitsquare.p2p.peers;
import com.google.common.util.concurrent.MoreExecutors;
import io.bitsquare.app.Log; import io.bitsquare.app.Log;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Message;
import io.bitsquare.p2p.NodeAddress; import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.*; import io.bitsquare.p2p.network.*;
import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest; import io.bitsquare.p2p.peers.messages.peers.GetPeersRequest;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -19,12 +23,16 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class PeerExchangeManager implements MessageListener, ConnectionListener { public class PeerExchangeManager implements MessageListener, ConnectionListener {
private static final Logger log = LoggerFactory.getLogger(PeerExchangeManager.class); 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 NetworkNode networkNode;
private final PeerManager peerManager; private final PeerManager peerManager;
private final Set<NodeAddress> seedNodeAddresses; private final Set<NodeAddress> seedNodeAddresses;
private final Map<NodeAddress, PeerExchangeHandshake> peerExchangeHandshakeMap = new HashMap<>(); private final Map<NodeAddress, PeerExchangeHandler> peerExchangeHandlerMap = new HashMap<>();
private Timer connectToMorePeersTimer; private Timer connectToMorePeersTimer;
private boolean shutDownInProgress; private boolean shutDownInProgress;
private ScheduledThreadPoolExecutor executor;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -38,15 +46,19 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses); this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
networkNode.addMessageListener(this); networkNode.addMessageListener(this);
networkNode.addConnectionListener(this);
} }
public void shutDown() { public void shutDown() {
Log.traceCall(); Log.traceCall();
shutDownInProgress = true; shutDownInProgress = true;
networkNode.removeMessageListener(this); networkNode.removeMessageListener(this);
networkNode.removeConnectionListener(this);
stopConnectToMorePeersTimer(); 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); remainingNodeAddresses.remove(nodeAddress);
Collections.shuffle(remainingNodeAddresses); Collections.shuffle(remainingNodeAddresses);
requestReportedPeers(nodeAddress, 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 // ConnectionListener implementation
@ -73,6 +90,12 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
@Override @Override
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) { 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 @Override
@ -88,41 +111,41 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
public void onMessage(Message message, Connection connection) { public void onMessage(Message message, Connection connection) {
if (message instanceof GetPeersRequest) { if (message instanceof GetPeersRequest) {
Log.traceCall(message.toString() + "\n\tconnection=" + connection); Log.traceCall(message.toString() + "\n\tconnection=" + connection);
PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, GetPeersRequestHandler getPeersRequestHandler = new GetPeersRequestHandler(networkNode,
peerManager, peerManager,
new PeerExchangeHandshake.Listener() { new GetPeersRequestHandler.Listener() {
@Override @Override
public void onComplete() { public void onComplete() {
log.trace("PeerExchangeHandshake of inbound connection complete.\n\tConnection={}", connection); log.trace("PeerExchangeHandshake of inbound connection complete.\n\tConnection={}", connection);
} }
@Override @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" + log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" +
"connection={}", errorMessage, connection); "connection={}", errorMessage, connection);
peerManager.handleConnectionFault(connection); peerManager.handleConnectionFault(connection);
} }
}); });
peerExchangeHandshake.onGetPeersRequest((GetPeersRequest) message, connection); getPeersRequestHandler.process((GetPeersRequest) message, connection);
} }
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Private // Request
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void requestReportedPeers(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) { private void requestReportedPeers(NodeAddress nodeAddress, List<NodeAddress> remainingNodeAddresses) {
Log.traceCall("nodeAddress=" + nodeAddress); Log.traceCall("nodeAddress=" + nodeAddress);
if (!peerExchangeHandshakeMap.containsKey(nodeAddress)) { if (!peerExchangeHandlerMap.containsKey(nodeAddress)) {
PeerExchangeHandshake peerExchangeHandshake = new PeerExchangeHandshake(networkNode, PeerExchangeHandler peerExchangeHandler = new PeerExchangeHandler(networkNode,
peerManager, peerManager,
new PeerExchangeHandshake.Listener() { new PeerExchangeHandler.Listener() {
@Override @Override
public void onComplete() { public void onComplete() {
log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress); log.trace("PeerExchangeHandshake of outbound connection complete. nodeAddress={}", nodeAddress);
peerExchangeHandshakeMap.remove(nodeAddress); peerExchangeHandlerMap.remove(nodeAddress);
connectToMorePeers(); requestWithAvailablePeers();
} }
@Override @Override
@ -130,26 +153,36 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" + log.trace("PeerExchangeHandshake of outbound connection failed.\n\terrorMessage={}\n\t" +
"nodeAddress={}", errorMessage, nodeAddress); "nodeAddress={}", errorMessage, nodeAddress);
peerExchangeHandshakeMap.remove(nodeAddress); peerExchangeHandlerMap.remove(nodeAddress);
peerManager.handleConnectionFault(nodeAddress, connection); peerManager.handleConnectionFault(nodeAddress, connection);
if (!shutDownInProgress) { if (!shutDownInProgress) {
if (!remainingNodeAddresses.isEmpty()) { if (!remainingNodeAddresses.isEmpty()) {
if (!peerManager.hasSufficientConnections()) {
log.info("There are remaining nodes available for requesting peers. " + log.info("There are remaining nodes available for requesting peers. " +
"We will try getReportedPeers again."); "We will try getReportedPeers again.");
requestReportedPeersFromRandomPeer(remainingNodeAddresses); 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 { } else {
log.info("There is no remaining node available for requesting peers. " + log.info("There is no remaining node available for requesting peers. " +
"That is expected if no other node is online.\n\t" + "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) if (connectToMorePeersTimer == null)
connectToMorePeersTimer = UserThread.runAfterRandomDelay( connectToMorePeersTimer = UserThread.runAfter(() -> {
PeerExchangeManager.this::connectToMorePeers, 20, 30); log.trace("ConnectToMorePeersTimer called from requestReportedPeers code path");
stopConnectToMorePeersTimer();
requestWithAvailablePeers();
}, RETRY_DELAY_SEC);
} }
} }
} }
}); });
peerExchangeHandshakeMap.put(nodeAddress, peerExchangeHandshake); peerExchangeHandlerMap.put(nodeAddress, peerExchangeHandler);
peerExchangeHandshake.requestConnectedPeers(nodeAddress); peerExchangeHandler.requestConnectedPeers(nodeAddress);
} else { } else {
//TODO check when that happens //TODO check when that happens
log.warn("We have started already a peerExchangeHandshake. " + log.warn("We have started already a peerExchangeHandshake. " +
@ -158,59 +191,86 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
} }
} }
private void requestWithAvailablePeers() {
private void connectToMorePeers() {
Log.traceCall(); Log.traceCall();
stopConnectToMorePeersTimer();
if (!peerManager.hasSufficientConnections()) { if (!peerManager.hasSufficientConnections()) {
// We create a new list of not connected candidates // We create a new list of not connected candidates
// 1. reported sorted by most recent lastActivityDate // 1. reported shuffled peers
// 2. persisted sorted by most recent lastActivityDate // 2. persisted shuffled peers
// 3. seenNodes // 3. Add as last shuffled seedNodes (least priority)
List<NodeAddress> list = new ArrayList<>(getFilteredAndSortedList(peerManager.getReportedPeers(), new ArrayList<>())); List<NodeAddress> list = getFilteredNonSeedNodeList(getNodeAddresses(peerManager.getReportedPeers()), new ArrayList<>());
list.addAll(getFilteredAndSortedList(peerManager.getPersistedPeers(), list)); Collections.shuffle(list);
ArrayList<NodeAddress> seedNodeAddresses = new ArrayList<>(this.seedNodeAddresses);
Collections.shuffle(seedNodeAddresses); List<NodeAddress> filteredPersistedPeers = getFilteredNonSeedNodeList(getNodeAddresses(peerManager.getPersistedPeers()), list);
list.addAll(seedNodeAddresses.stream() Collections.shuffle(filteredPersistedPeers);
.filter(e -> !list.contains(e) && list.addAll(filteredPersistedPeers);
!peerManager.isSelf(e) &&
!peerManager.isConfirmed(e)) List<NodeAddress> filteredSeedNodeAddresses = getFilteredList(new ArrayList<>(seedNodeAddresses), list);
.collect(Collectors.toSet())); Collections.shuffle(filteredSeedNodeAddresses);
log.info("Sorted and filtered list: list.size()=" + list.size()); list.addAll(filteredSeedNodeAddresses);
log.trace("Sorted and filtered list: list=" + list);
log.info("Number of peers in list for connectToMorePeers: {}", list.size());
log.trace("Filtered connectToMorePeers list: list=" + list);
if (!list.isEmpty()) { if (!list.isEmpty()) {
// Dont shuffle as we want the seed nodes at the last entries
NodeAddress nextCandidate = list.get(0); NodeAddress nextCandidate = list.get(0);
list.remove(nextCandidate); list.remove(nextCandidate);
requestReportedPeers(nextCandidate, list); requestReportedPeers(nextCandidate, list);
} else { } 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 { } else {
log.info("We have already sufficient connections."); log.info("We have already sufficient connections.");
} }
} }
// sorted by most recent lastActivityDate
private List<NodeAddress> getFilteredAndSortedList(Set<ReportedPeer> set, List<NodeAddress> list) { ///////////////////////////////////////////////////////////////////////////////////////////
return set.stream() // Maintenance
.filter(e -> !list.contains(e.nodeAddress) && ///////////////////////////////////////////////////////////////////////////////////////////
!peerManager.isSeedNode(e) &&
!peerManager.isSelf(e) && private void requestAgain() {
!peerManager.isConfirmed(e)) checkNotNull(networkNode.getNodeAddress(), "My node address must not be null at sendUpdateRequest");
.collect(Collectors.toList()) Set<NodeAddress> candidates = new HashSet<>(getNodeAddresses(peerManager.getReportedPeers()));
.stream() candidates.addAll(getNodeAddresses(peerManager.getPersistedPeers()));
.filter(e -> e.lastActivityDate != null) candidates.addAll(seedNodeAddresses);
.sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate)) candidates.remove(networkNode.getNodeAddress());
ArrayList<NodeAddress> list = new ArrayList<>(candidates);
Collections.shuffle(list);
NodeAddress candidate = list.remove(0);
requestReportedPeers(candidate, list);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////////////////////
private List<NodeAddress> getNodeAddresses(Collection<ReportedPeer> collection) {
return collection.stream()
.map(e -> e.nodeAddress) .map(e -> e.nodeAddress)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private void requestReportedPeersFromRandomPeer(List<NodeAddress> remainingNodeAddresses) { private List<NodeAddress> getFilteredList(Collection<NodeAddress> collection, List<NodeAddress> list) {
NodeAddress nextCandidate = remainingNodeAddresses.get(new Random().nextInt(remainingNodeAddresses.size())); return collection.stream()
remainingNodeAddresses.remove(nextCandidate); .filter(e -> !list.contains(e) &&
requestReportedPeers(nextCandidate, remainingNodeAddresses); !peerManager.isSelf(e) &&
!peerManager.isConfirmed(e))
.collect(Collectors.toList());
}
private List<NodeAddress> getFilteredNonSeedNodeList(Collection<NodeAddress> collection, List<NodeAddress> list) {
return getFilteredList(collection, list).stream()
.filter(e -> !peerManager.isSeedNode(e))
.collect(Collectors.toList());
} }
private void stopConnectToMorePeersTimer() { private void stopConnectToMorePeersTimer() {

View file

@ -15,7 +15,6 @@ import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -32,6 +31,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
private static int MAX_CONNECTIONS_EXTENDED_1; private static int MAX_CONNECTIONS_EXTENDED_1;
private static int MAX_CONNECTIONS_EXTENDED_2; private static int MAX_CONNECTIONS_EXTENDED_2;
private static int MAX_CONNECTIONS_EXTENDED_3; private static int MAX_CONNECTIONS_EXTENDED_3;
private boolean printReportedPeersDetails = true;
public static void setMaxConnections(int maxConnections) { public static void setMaxConnections(int maxConnections) {
MAX_CONNECTIONS = maxConnections; MAX_CONNECTIONS = maxConnections;
@ -271,87 +271,48 @@ public class PeerManager implements ConnectionListener, MessageListener {
} }
public void addToReportedPeers(HashSet<ReportedPeer> reportedPeersToAdd, Connection connection) { public void addToReportedPeers(HashSet<ReportedPeer> reportedPeersToAdd, Connection connection) {
// we disconnect misbehaving nodes trying to send too many peers printReportedPeers(reportedPeersToAdd);
// 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<ReportedPeer, ReportedPeer> reportedPeersMap = reportedPeers.stream()
.collect(Collectors.toMap(e -> e, Function.identity()));
HashSet<ReportedPeer> 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);
// 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(); purgeReportedPeersIfExceeds();
persistedPeers.addAll(reportedPeersToAdd); persistedPeers.addAll(reportedPeersToAdd);
persistedPeers.addAll(new HashSet<>(getConnectedPeers())); purgePersistedPeersIfExceeds();
// 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<ReportedPeer> 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));
}
}
}
}
if (dbStorage != null) if (dbStorage != null)
dbStorage.queueUpForSave(persistedPeers, 2000); 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() { private void printReportedPeers() {
if (!reportedPeers.isEmpty()) { if (!reportedPeers.isEmpty()) {
if (printReportedPeersDetails) {
StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" +
"Reported peers:"); "Collected reported peers:");
reportedPeers.stream().forEach(e -> result.append("\n").append(e)); reportedPeers.stream().forEach(e -> result.append("\n").append(e));
result.append("\n------------------------------------------------------------\n"); result.append("\n------------------------------------------------------------\n");
//log.trace(result.toString()); log.info(result.toString());
log.info("Number of reported peers: {}", reportedPeers.size()); }
log.info("Number of collected reported peers: {}", reportedPeers.size());
} }
} }
public void printReportedPeers(HashSet<ReportedPeer> 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 // Persisted peers
@ -455,6 +416,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
return isConfirmed(reportedPeer.nodeAddress); return isConfirmed(reportedPeer.nodeAddress);
} }
// Checks if that connection has the peers node address
public boolean isConfirmed(NodeAddress nodeAddress) { public boolean isConfirmed(NodeAddress nodeAddress) {
return networkNode.getNodeAddressesOfConfirmedConnections().contains(nodeAddress); return networkNode.getNodeAddressesOfConfirmedConnections().contains(nodeAddress);
} }
@ -480,22 +442,41 @@ public class PeerManager implements ConnectionListener, MessageListener {
private void purgeReportedPeersIfExceeds() { private void purgeReportedPeersIfExceeds() {
Log.traceCall(); Log.traceCall();
int size = getReportedPeers().size(); int size = getReportedPeers().size();
if (size > MAX_REPORTED_PEERS) { int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_EXTENDED_3;
log.trace("We have more then {} reported peers. size={}. " + if (size > limit) {
"We remove random peers from the reported peers list.", MAX_REPORTED_PEERS, size); log.trace("We have already {} reported peers which exceeds our limit of {}." +
int diff = size - MAX_REPORTED_PEERS; "We remove random peers from the reported peers list.", size, limit);
int diff = size - limit;
List<ReportedPeer> list = new ArrayList<>(getReportedPeers()); List<ReportedPeer> list = new ArrayList<>(getReportedPeers());
// we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random // we dont use sorting by lastActivityDate to avoid attack vectors and keep it more random
for (int i = 0; i < diff; i++) { for (int i = 0; i < diff; i++) {
ReportedPeer toRemove = getAndRemoveRandomReportedPeer(list); ReportedPeer toRemove = getAndRemoveRandomReportedPeer(list);
removeReportedPeer(toRemove); removeReportedPeer(toRemove);
removePersistedPeer(toRemove);
} }
} else { } else {
log.trace("No need to purge reported peers.\n\tWe don't have more then {} reported peers yet.", MAX_REPORTED_PEERS); 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<ReportedPeer> 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<ReportedPeer> list) { private ReportedPeer getAndRemoveRandomReportedPeer(List<ReportedPeer> list) {
checkArgument(!list.isEmpty(), "List must not be empty"); checkArgument(!list.isEmpty(), "List must not be empty");
return list.remove(new Random().nextInt(list.size())); return list.remove(new Random().nextInt(list.size()));
@ -509,6 +490,18 @@ public class PeerManager implements ConnectionListener, MessageListener {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
public HashSet<ReportedPeer> getConnectedPeersNonSeedNodes() {
return new HashSet<>(getConnectedPeers().stream()
.filter(e -> !isSeedNode(e))
.collect(Collectors.toSet()));
}
public HashSet<ReportedPeer> getConnectedPeersNonSeedNodes(NodeAddress excludedNodeAddress) {
return new HashSet<>(getConnectedPeersNonSeedNodes().stream()
.filter(e -> !e.nodeAddress.equals(excludedNodeAddress))
.collect(Collectors.toSet()));
}
private void stopCheckMaxConnectionsTimer() { private void stopCheckMaxConnectionsTimer() {
if (checkMaxConnectionsTimer != null) { if (checkMaxConnectionsTimer != null) {
checkMaxConnectionsTimer.cancel(); checkMaxConnectionsTimer.cancel();

View file

@ -22,6 +22,9 @@ import static com.google.common.base.Preconditions.checkArgument;
public class RequestDataManager implements MessageListener { public class RequestDataManager implements MessageListener {
private static final Logger log = LoggerFactory.getLogger(RequestDataManager.class); private static final Logger log = LoggerFactory.getLogger(RequestDataManager.class);
private static final long RETRY_DELAY_SEC = 10;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Listener // Listener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -193,7 +196,7 @@ public class RequestDataManager implements MessageListener {
// try again after a pause // try again after a pause
stopRequestDataTimer(); stopRequestDataTimer();
requestDataTimer = UserThread.runAfterRandomDelay(() -> { requestDataTimer = UserThread.runAfter(() -> {
log.trace("requestDataAfterDelayTimer called"); log.trace("requestDataAfterDelayTimer called");
// We want to keep it sorted but avoid duplicates // 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 // 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); list.remove(nextCandidate);
requestData(nextCandidate, list); requestData(nextCandidate, list);
}, },
10, 15, TimeUnit.SECONDS); RETRY_DELAY_SEC, TimeUnit.SECONDS);
} }
requestDataHandshakeMap.remove(nodeAddress); requestDataHandshakeMap.remove(nodeAddress);

View file

@ -32,14 +32,12 @@ public class SeedNodesRepository {
new NodeAddress("izs5oz7i5ta7c2ir.onion:8000"),*/ new NodeAddress("izs5oz7i5ta7c2ir.onion:8000"),*/
// v0.3.5 // v0.3.5
new NodeAddress("que4ysbd2qazkb7d.onion:8000"), new NodeAddress("hulvbm5xjn7b7ku4.onion:8000"),
new NodeAddress("h2crs2j5huhclkc6.onion:8000"), new NodeAddress("3efgjjbdvhbvck3x.onion:8000"),
new NodeAddress("7a3sj4j6yw5oukai.onion:8000"), new NodeAddress("3unfcshgwipxhxfm.onion:8000"),
// testnet // testnet
new NodeAddress("znmy44wcstn2rkva.onion:8001"), new NodeAddress("znmy44wcstn2rkva.onion:8001"),
/* new NodeAddress("zvn7umikgxml6x6h.onion:8001"),
new NodeAddress("wnfxmrmsyeeos2dy.onion:8001"),*/
// regtest // regtest
// For development you need to change that to your local onion addresses // For development you need to change that to your local onion addresses

View file

@ -15,7 +15,10 @@ public class ProtectedData implements Serializable {
private static final Logger log = LoggerFactory.getLogger(P2PDataStorage.class); private static final Logger log = LoggerFactory.getLogger(P2PDataStorage.class);
public final ExpirableMessage expirableMessage; public final ExpirableMessage expirableMessage;
//TODO check if that field make sense as it is in expirableMessage.getTTL()
transient public long ttl; transient public long ttl;
public final PublicKey ownerPubKey; public final PublicKey ownerPubKey;
public final int sequenceNumber; public final int sequenceNumber;
public final byte[] signature; public final byte[] signature;