Bugfixes, refactorings,...

This commit is contained in:
Manfred Karrer 2016-01-30 23:43:58 +01:00
parent d933110a8d
commit 9dd9decb96
22 changed files with 124 additions and 82 deletions

View file

@ -121,7 +121,7 @@ public class AddressEntry implements Serializable {
// For display we usually only display the first 8 characters. // For display we usually only display the first 8 characters.
@Nullable @Nullable
public String getShortOfferId() { public String getShortOfferId() {
return offerId != null ? offerId.substring(0, 8) : null; return offerId != null ? offerId.substring(0, Math.min(8, offerId.length())) : null;
} }
public Context getContext() { public Context getContext() {

View file

@ -968,7 +968,7 @@ public class TradeWalletService {
checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null"); checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null");
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
checkNotNull(sigKey, "sigKey must not be null"); checkNotNull(sigKey, "signInput: sigKey must not be null. input.getOutpoint()=" + input.getOutpoint().toString());
Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false); Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature signature = sigKey.sign(hash, aesKey); ECKey.ECDSASignature signature = sigKey.sign(hash, aesKey);
TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false); TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false);

View file

@ -26,6 +26,7 @@ public class BlockChainAccountContractData extends PaymentAccountContractData im
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION; private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
private String address; private String address;
// used in crypto note coins. not supported now but hopefully in future, so leave it for now.
private String paymentId; private String paymentId;
public BlockChainAccountContractData(String paymentMethod, String id, int maxTradePeriod) { public BlockChainAccountContractData(String paymentMethod, String id, int maxTradePeriod) {

View file

@ -198,7 +198,7 @@ public final class Offer implements PubKeyProtectedExpirablePayload {
} }
public String getReferenceText() { public String getReferenceText() {
return id.substring(0, 8); return id.substring(0, Math.min(8, id.length()));
} }
@ -263,7 +263,7 @@ public final class Offer implements PubKeyProtectedExpirablePayload {
} }
public String getShortId() { public String getShortId() {
return id.substring(0, 8); return id.substring(0, Math.min(8, id.length()));
} }
public NodeAddress getOffererNodeAddress() { public NodeAddress getOffererNodeAddress() {

View file

@ -66,7 +66,7 @@ public class ProcessPayDepositRequest extends TradeTask {
// We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID // We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID
if (paymentAccountContractData instanceof BlockChainAccountContractData && if (paymentAccountContractData instanceof BlockChainAccountContractData &&
CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) { CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) {
String paymentId = Hash.getHashAsHex(trade.getId()).substring(0, 32); String paymentId = Hash.getHashAsHex(trade.getId()).substring(0, Math.min(32, trade.getId().length()));
((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId); ((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId);
} }

View file

@ -54,7 +54,7 @@ public class ProcessPublishDepositTxRequest extends TradeTask {
// We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID // We apply the payment ID in case its a cryptoNote coin. It is created form the hash of the trade ID
if (paymentAccountContractData instanceof BlockChainAccountContractData && if (paymentAccountContractData instanceof BlockChainAccountContractData &&
CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) { CurrencyUtil.isCryptoNoteCoin(processModel.getOffer().getCurrencyCode())) {
String paymentId = Hash.getHashAsHex(trade.getId()).substring(0, 32); String paymentId = Hash.getHashAsHex(trade.getId()).substring(0, Math.min(32, trade.getId().length()));
((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId); ((BlockChainAccountContractData) paymentAccountContractData).setPaymentId(paymentId);
} }

View file

@ -27,13 +27,11 @@ import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import java.util.function.Consumer;
public class TextFieldWithCopyIcon extends AnchorPane { public class TextFieldWithCopyIcon extends AnchorPane {
private final StringProperty text = new SimpleStringProperty(); private final StringProperty text = new SimpleStringProperty();
private final TextField textField; private final TextField textField;
private Consumer<String> handler; private boolean copyWithoutCurrencyPostFix;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -48,10 +46,19 @@ public class TextFieldWithCopyIcon extends AnchorPane {
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
AnchorPane.setRightAnchor(copyIcon, 0.0); AnchorPane.setRightAnchor(copyIcon, 0.0);
copyIcon.setOnMouseClicked(e -> { copyIcon.setOnMouseClicked(e -> {
if (getText() != null && getText().length() > 0) { String text = getText();
Utilities.copyToClipboard(getText()); if (text != null && text.length() > 0) {
if (handler != null) String copyText;
handler.accept(getText()); if (copyWithoutCurrencyPostFix) {
String[] strings = text.split(" ");
if (strings.length > 1)
copyText = strings[0]; // exclude the BTC postfix
else
copyText = text;
} else {
copyText = text;
}
Utilities.copyToClipboard(copyText);
} }
}); });
textField = new TextField(); textField = new TextField();
@ -85,7 +92,8 @@ public class TextFieldWithCopyIcon extends AnchorPane {
this.text.set(text); this.text.set(text);
} }
public void setHandler(Consumer<String> handler) { public void setCopyWithoutCurrencyPostFix(boolean copyWithoutCurrencyPostFix) {
this.handler = handler; this.copyWithoutCurrencyPostFix = copyWithoutCurrencyPostFix;
} }
} }

View file

@ -54,20 +54,20 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
public static final String TITLE_KEY = "view.title"; public static final String TITLE_KEY = "view.title";
public static BorderPane getBaseApplicationContainer() { public static StackPane getRootContainer() {
return baseApplicationContainer; return MainView.rootContainer;
} }
public static void blur() { public static void blur() {
transitions.blur(MainView.base); transitions.blur(MainView.rootContainer);
} }
public static void blurLight() { public static void blurLight() {
transitions.blur(MainView.base, Transitions.DEFAULT_DURATION, true, false, 5); transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, true, false, 5);
} }
public static void removeBlur() { public static void removeBlur() {
transitions.removeBlur(baseApplicationContainer); transitions.removeBlur(MainView.rootContainer);
} }
private final ToggleGroup navButtons = new ToggleGroup(); private final ToggleGroup navButtons = new ToggleGroup();
@ -86,9 +86,9 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
private ProgressBar btcSyncIndicator; private ProgressBar btcSyncIndicator;
private Label btcSplashInfo; private Label btcSplashInfo;
private List<String> persistedFilesCorrupted; private List<String> persistedFilesCorrupted;
private static BorderPane baseApplicationContainer; private BorderPane baseApplicationContainer;
private static StackPane base;
private Popup p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup; private Popup p2PNetworkWarnMsgPopup, btcNetworkWarnMsgPopup;
private static StackPane rootContainer;
@Inject @Inject
public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, Transitions transitions, public MainView(MainViewModel model, CachingViewLoader viewLoader, Navigation navigation, Transitions transitions,
@ -102,7 +102,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
@Override @Override
protected void initialize() { protected void initialize() {
MainView.base = this.root; MainView.rootContainer = this.root;
ToggleButton marketButton = new NavButton(MarketView.class, "Market"); ToggleButton marketButton = new NavButton(MarketView.class, "Market");
ToggleButton buyButton = new NavButton(BuyOfferView.class, "Buy BTC"); ToggleButton buyButton = new NavButton(BuyOfferView.class, "Buy BTC");

View file

@ -225,7 +225,8 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
private void onRequestUpload() { private void onRequestUpload() {
if (tempAttachments.size() < 3) { if (tempAttachments.size() < 3) {
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open file to attach"); int maxSizeInKB = Connection.getMaxMsgSize() / 1024;
fileChooser.setTitle("Open file to attach (max. file size: " + maxSizeInKB + " kb)");
/* if (Utilities.isUnix()) /* if (Utilities.isUnix())
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));*/ fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));*/
File result = fileChooser.showOpenDialog(stage); File result = fileChooser.showOpenDialog(stage);
@ -238,7 +239,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
tempAttachments.add(new DisputeDirectMessage.Attachment(result.getName(), filesAsBytes)); tempAttachments.add(new DisputeDirectMessage.Attachment(result.getName(), filesAsBytes));
inputTextArea.setText(inputTextArea.getText() + "\n[Attachment " + result.getName() + "]"); inputTextArea.setText(inputTextArea.getText() + "\n[Attachment " + result.getName() + "]");
} else { } else {
new Popup().error("The max. allowed file size is 100 kB.").show(); new Popup().error("The max. allowed file size is " + maxSizeInKB + " kB.").show();
} }
} catch (java.io.IOException e) { } catch (java.io.IOException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -19,6 +19,7 @@ package io.bitsquare.gui.main.portfolio.pendingtrades.steps;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.common.util.Tuple3; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.components.TextFieldWithCopyIcon;
import io.bitsquare.gui.components.TitledGroupBg; import io.bitsquare.gui.components.TitledGroupBg;
import io.bitsquare.gui.components.TxIdTextField; import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.components.paymentmethods.*; import io.bitsquare.gui.components.paymentmethods.*;
@ -208,8 +209,9 @@ public class StartPaymentView extends TradeStepDetailsView {
txIdTextField = addLabelTxIdTextField(gridPane, gridRow, "Deposit transaction ID:", Layout.FIRST_ROW_DISTANCE).second; txIdTextField = addLabelTxIdTextField(gridPane, gridRow, "Deposit transaction ID:", Layout.FIRST_ROW_DISTANCE).second;
TitledGroupBg accountTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 1, "Payments details", Layout.GROUP_DISTANCE); TitledGroupBg accountTitledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 1, "Payments details", Layout.GROUP_DISTANCE);
addLabelTextFieldWithCopyIcon(gridPane, gridRow, "Amount to transfer:", model.getFiatAmount(), TextFieldWithCopyIcon field = addLabelTextFieldWithCopyIcon(gridPane, gridRow, "Amount to transfer:", model.getFiatAmount(),
Layout.FIRST_ROW_AND_GROUP_DISTANCE); Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
field.setCopyWithoutCurrencyPostFix(true);
PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData(); PaymentAccountContractData paymentAccountContractData = model.dataModel.getSellersPaymentAccountContractData();
String paymentMethodName = paymentAccountContractData.getPaymentMethodName(); String paymentMethodName = paymentAccountContractData.getPaymentMethodName();

View file

@ -103,6 +103,7 @@ public class ContractPopup extends Popup {
if (sellerPaymentAccountContractData instanceof BlockChainAccountContractData && if (sellerPaymentAccountContractData instanceof BlockChainAccountContractData &&
((BlockChainAccountContractData) sellerPaymentAccountContractData).getPaymentId() != null) { ((BlockChainAccountContractData) sellerPaymentAccountContractData).getPaymentId() != null) {
rows++; rows++;
isPaymentIdAvailable = true;
} }
addTitledGroupBg(gridPane, ++rowIndex, rows, "Contract"); addTitledGroupBg(gridPane, ++rowIndex, rows, "Contract");
addLabelTextFieldWithCopyIcon(gridPane, rowIndex, "Offer ID:", offer.getId(), addLabelTextFieldWithCopyIcon(gridPane, rowIndex, "Offer ID:", offer.getId(),
@ -123,7 +124,7 @@ public class ContractPopup extends Popup {
addLabelTextField(gridPane, ++rowIndex, "Selected arbitrator:", contract.arbitratorNodeAddress.getFullAddress()); addLabelTextField(gridPane, ++rowIndex, "Selected arbitrator:", contract.arbitratorNodeAddress.getFullAddress());
addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Buyer payment details:", addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Buyer payment details:",
BSResources.get(contract.getBuyerPaymentAccountContractData().getPaymentDetails())).second.setMouseTransparent(false); BSResources.get(contract.getBuyerPaymentAccountContractData().getPaymentDetails())).second.setMouseTransparent(false);
addLabelTextField(gridPane, ++rowIndex, "Seller payment details:", addLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, "Seller payment details:",
BSResources.get(sellerPaymentAccountContractData.getPaymentDetails())).second.setMouseTransparent(false); BSResources.get(sellerPaymentAccountContractData.getPaymentDetails())).second.setMouseTransparent(false);
if (isPaymentIdAvailable) if (isPaymentIdAvailable)
addLabelTextField(gridPane, ++rowIndex, "Seller payment ID:", addLabelTextField(gridPane, ++rowIndex, "Seller payment ID:",

View file

@ -208,7 +208,7 @@ public class Popup {
protected void createPopup() { protected void createPopup() {
if (owner == null) if (owner == null)
owner = MainView.getBaseApplicationContainer(); owner = MainView.getRootContainer();
stage = new Stage(); stage = new Stage();
Scene scene = new Scene(gridPane); Scene scene = new Scene(gridPane);

View file

@ -28,7 +28,7 @@ public class NodeAddress implements Serializable {
// We use just a few chars form or address to blur the potential receiver for sent messages // We use just a few chars form or address to blur the potential receiver for sent messages
public byte[] getAddressPrefixHash() { public byte[] getAddressPrefixHash() {
if (addressPrefixHash == null) if (addressPrefixHash == null)
addressPrefixHash = Hash.getHash(getFullAddress().substring(0, 2)); addressPrefixHash = Hash.getHash(getFullAddress().substring(0, Math.min(2, getFullAddress().length())));
return addressPrefixHash; return addressPrefixHash;
} }

View file

@ -80,6 +80,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private ChangeListener<NodeAddress> connectionNodeAddressListener; private ChangeListener<NodeAddress> connectionNodeAddressListener;
private Subscription networkReadySubscription; private Subscription networkReadySubscription;
private boolean isBootstrapped; private boolean isBootstrapped;
private ChangeListener<Number> numOfBroadcastsChangeListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -537,18 +538,35 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
log.debug("remove result=" + result); log.debug("remove result=" + result);
sendMailboxMessageListener.onFault("A timeout occurred when trying to broadcast mailbox data."); sendMailboxMessageListener.onFault("A timeout occurred when trying to broadcast mailbox data.");
}, 30); }, 30);
broadcaster.addOneTimeListener(message -> { Broadcaster.Listener listener = message -> {
if (message instanceof AddDataMessage && if (message instanceof AddDataMessage &&
((AddDataMessage) message).data.equals(protectedMailboxData)) { ((AddDataMessage) message).data.equals(protectedMailboxData)) {
sendMailboxMessageListener.onStoredInMailbox(); sendMailboxMessageListener.onStoredInMailbox();
sendMailboxMessageTimeoutTimer.cancel(); sendMailboxMessageTimeoutTimer.cancel();
} }
}); };
broadcaster.addListener(listener);
if (numOfBroadcastsChangeListener != null) {
log.warn("numOfBroadcastsChangeListener should be null");
broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener);
}
numOfBroadcastsChangeListener = (observable, oldValue, newValue) -> {
// We want to get at least 1 successful broadcast
if ((int) newValue > 0)
broadcaster.removeListener(listener);
UserThread.execute(() -> {
broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener);
numOfBroadcastsChangeListener = null;
});
};
broadcaster.getNumOfBroadcastsProperty().addListener(numOfBroadcastsChangeListener);
boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress()); boolean result = p2PDataStorage.add(protectedMailboxData, networkNode.getNodeAddress());
if (!result) { if (!result) {
sendMailboxMessageTimeoutTimer.cancel(); sendMailboxMessageTimeoutTimer.cancel();
broadcaster.removeListener(listener);
broadcaster.getNumOfBroadcastsProperty().removeListener(numOfBroadcastsChangeListener);
sendMailboxMessageListener.onFault("Data already exists in our local database"); sendMailboxMessageListener.onFault("Data already exists in our local database");
boolean result2 = p2PDataStorage.remove(protectedMailboxData, networkNode.getNodeAddress()); boolean result2 = p2PDataStorage.remove(protectedMailboxData, networkNode.getNodeAddress());
log.debug("remove result=" + result2); log.debug("remove result=" + result2);

View file

@ -3,7 +3,8 @@ package io.bitsquare.p2p.network;
public enum IllegalRequest { public enum IllegalRequest {
MaxSizeExceeded(1), MaxSizeExceeded(1),
InvalidDataType(0), InvalidDataType(0),
WrongNetworkId(0); WrongNetworkId(0),
TooManyMessages(1);
public final int maxTolerance; public final int maxTolerance;

View file

@ -107,7 +107,6 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
return outboundConnection; return outboundConnection;
} catch (Throwable throwable) { } catch (Throwable throwable) {
if (!(throwable instanceof ConnectException || throwable instanceof IOException)) { if (!(throwable instanceof ConnectException || throwable instanceof IOException)) {
throwable.printStackTrace();
log.error("Executing task failed. " + throwable.getMessage()); log.error("Executing task failed. " + throwable.getMessage());
} }
throw throwable; throw throwable;

View file

@ -8,6 +8,8 @@ import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.network.Connection; import io.bitsquare.p2p.network.Connection;
import io.bitsquare.p2p.network.NetworkNode; import io.bitsquare.p2p.network.NetworkNode;
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage; import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -27,12 +29,16 @@ public class Broadcaster {
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final Set<Listener> listeners = new CopyOnWriteArraySet<>(); private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
private IntegerProperty numOfBroadcasts = new SimpleIntegerProperty(0);
public Broadcaster(NetworkNode networkNode) { public Broadcaster(NetworkNode networkNode) {
this.networkNode = networkNode; this.networkNode = networkNode;
} }
public void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) { public void broadcast(DataBroadcastMessage message, @Nullable NodeAddress sender) {
Log.traceCall("Sender " + sender + ". Message " + message.toString()); Log.traceCall("Sender " + sender + ". Message " + message.toString());
numOfBroadcasts.set(0);
Set<Connection> receivers = networkNode.getConfirmedConnections(); Set<Connection> receivers = networkNode.getConfirmedConnections();
if (!receivers.isEmpty()) { if (!receivers.isEmpty()) {
log.info("Broadcast message to {} peers. Message: {}", receivers.size(), message); log.info("Broadcast message to {} peers. Message: {}", receivers.size(), message);
@ -46,9 +52,9 @@ public class Broadcaster {
@Override @Override
public void onSuccess(Connection connection) { public void onSuccess(Connection connection) {
log.trace("Broadcast to " + connection + " succeeded."); log.trace("Broadcast to " + connection + " succeeded.");
numOfBroadcasts.set(numOfBroadcasts.get() + 1);
listeners.stream().forEach(listener -> { listeners.stream().forEach(listener -> {
listener.onBroadcasted(message); listener.onBroadcasted(message);
listeners.remove(listener);
}); });
} }
@ -64,9 +70,16 @@ public class Broadcaster {
} }
} }
public IntegerProperty getNumOfBroadcastsProperty() {
return numOfBroadcasts;
}
// That listener gets immediately removed after the handler is called // That listener gets immediately removed after the handler is called
public void addOneTimeListener(Listener listener) { public void addListener(Listener listener) {
listeners.add(listener); listeners.add(listener);
} }
public void removeListener(Listener listener) {
listeners.remove(listener);
}
} }

View file

@ -253,6 +253,7 @@ public class PeerExchangeManager implements MessageListener, ConnectionListener
!peerManager.isConfirmed(e)) !peerManager.isConfirmed(e))
.collect(Collectors.toList()) .collect(Collectors.toList())
.stream() .stream()
.filter(e -> e.lastActivityDate != null)
.sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate)) .sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate))
.map(e -> e.nodeAddress) .map(e -> e.nodeAddress)
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -8,7 +8,6 @@ import io.bitsquare.p2p.network.*;
import io.bitsquare.p2p.peers.messages.data.UpdateDataRequest; import io.bitsquare.p2p.peers.messages.data.UpdateDataRequest;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -44,13 +43,13 @@ public class PeerManager implements ConnectionListener, MessageListener {
private static final int MAX_REPORTED_PEERS = 1000; private static final int MAX_REPORTED_PEERS = 1000;
private static final int MAX_PERSISTED_PEERS = 500; private static final int MAX_PERSISTED_PEERS = 500;
private static final long MAX_AGE = 14 * 24 * 60 * 60 * 1000; // max age for reported peers is 14 days private static final long DAY = 24 * 60 * 60 * 1000; // max age for reported peers is 14 days
private static final long MAX_AGE = 14 * DAY; // max age for reported peers is 14 days
private final NetworkNode networkNode; private final NetworkNode networkNode;
private final Set<NodeAddress> seedNodeAddresses; private final Set<NodeAddress> seedNodeAddresses;
@Nullable private final Storage<HashSet<ReportedPeer>> dbStorage;
private Storage<HashSet<ReportedPeer>> dbStorage;
private final HashSet<ReportedPeer> persistedPeers = new HashSet<>(); private final HashSet<ReportedPeer> persistedPeers = new HashSet<>();
private final Set<ReportedPeer> reportedPeers = new HashSet<>(); private final Set<ReportedPeer> reportedPeers = new HashSet<>();
@ -66,7 +65,12 @@ public class PeerManager implements ConnectionListener, MessageListener {
this.networkNode = networkNode; this.networkNode = networkNode;
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses); this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
networkNode.addConnectionListener(this); networkNode.addConnectionListener(this);
createDbStorage(storageDir); dbStorage = new Storage<>(storageDir);
HashSet<ReportedPeer> persistedPeers = dbStorage.initAndGetPersisted("persistedPeers");
if (persistedPeers != null) {
log.info("We have persisted reported peers. persistedPeers.size()=" + persistedPeers.size());
this.persistedPeers.addAll(persistedPeers);
}
connectionNodeAddressListener = (observable, oldValue, newValue) -> { connectionNodeAddressListener = (observable, oldValue, newValue) -> {
// Every time we get a new peer connected with a known address we check if we need to remove peers // Every time we get a new peer connected with a known address we check if we need to remove peers
@ -79,21 +83,6 @@ public class PeerManager implements ConnectionListener, MessageListener {
}; };
} }
private void createDbStorage(File storageDir) {
dbStorage = new Storage<>(storageDir);
initPersistedPeers();
}
private void initPersistedPeers() {
if (dbStorage != null) {
HashSet<ReportedPeer> persistedPeers = dbStorage.initAndGetPersisted("persistedPeers");
if (persistedPeers != null) {
log.info("We have persisted reported peers. persistedPeers.size()=" + persistedPeers.size());
this.persistedPeers.addAll(persistedPeers);
}
}
}
public void shutDown() { public void shutDown() {
Log.traceCall(); Log.traceCall();
@ -124,10 +113,15 @@ public class PeerManager implements ConnectionListener, MessageListener {
public void onDisconnect(Reason reason, Connection connection) { public void onDisconnect(Reason reason, Connection connection) {
connection.getNodeAddressProperty().removeListener(connectionNodeAddressListener); connection.getNodeAddressProperty().removeListener(connectionNodeAddressListener);
connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> { connection.getPeersNodeAddressOptional().ifPresent(nodeAddress -> {
ReportedPeer reportedPeer = new ReportedPeer(nodeAddress); penalizeUnreachablePeer(nodeAddress);
Optional<ReportedPeer> reportedPeerOptional = reportedPeers.stream()
.filter(e -> e.nodeAddress.equals(nodeAddress)).findAny();
if (reportedPeerOptional.isPresent()) {
ReportedPeer reportedPeer = reportedPeerOptional.get();
reportedPeers.remove(reportedPeer); reportedPeers.remove(reportedPeer);
persistedPeers.add(reportedPeer); persistedPeers.add(reportedPeer);
dbStorage.queueUpForSave(persistedPeers, 5000); dbStorage.queueUpForSave(persistedPeers, 5000);
}
}); });
} }
@ -215,12 +209,14 @@ public class PeerManager implements ConnectionListener, MessageListener {
private void removeTooOldReportedPeers() { private void removeTooOldReportedPeers() {
Set<ReportedPeer> reportedPeersToRemove = reportedPeers.stream() Set<ReportedPeer> reportedPeersToRemove = reportedPeers.stream()
.filter(reportedPeer -> new Date().getTime() - reportedPeer.lastActivityDate.getTime() > MAX_AGE) .filter(reportedPeer -> reportedPeer.lastActivityDate != null &&
new Date().getTime() - reportedPeer.lastActivityDate.getTime() > MAX_AGE)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
reportedPeersToRemove.forEach(this::removeReportedPeer); reportedPeersToRemove.forEach(this::removeReportedPeer);
Set<ReportedPeer> persistedPeersToRemove = persistedPeers.stream() Set<ReportedPeer> persistedPeersToRemove = persistedPeers.stream()
.filter(reportedPeer -> new Date().getTime() - reportedPeer.lastActivityDate.getTime() > MAX_AGE) .filter(reportedPeer -> reportedPeer.lastActivityDate != null &&
new Date().getTime() - reportedPeer.lastActivityDate.getTime() > MAX_AGE)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
persistedPeersToRemove.forEach(this::removeFromPersistedPeers); persistedPeersToRemove.forEach(this::removeFromPersistedPeers);
} }
@ -312,6 +308,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
if (toRemove2 > 0) { if (toRemove2 > 0) {
// now we remove second half with a list sorted by oldest lastActivityDate // now we remove second half with a list sorted by oldest lastActivityDate
list = new ArrayList<>(persistedPeers); list = new ArrayList<>(persistedPeers);
list = list.stream().filter(e -> e.lastActivityDate != null).collect(Collectors.toList());
list.sort((o1, o2) -> o1.lastActivityDate.compareTo(o2.lastActivityDate)); list.sort((o1, o2) -> o1.lastActivityDate.compareTo(o2.lastActivityDate));
for (int i = 0; i < toRemove2; i++) { for (int i = 0; i < toRemove2; i++) {
persistedPeers.remove(list.get(i)); persistedPeers.remove(list.get(i));
@ -330,10 +327,11 @@ public class PeerManager implements ConnectionListener, MessageListener {
private void printReportedPeers() { private void printReportedPeers() {
if (!reportedPeers.isEmpty()) { if (!reportedPeers.isEmpty()) {
StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" + StringBuilder result = new StringBuilder("\n\n------------------------------------------------------------\n" +
"Reported peers for node " + networkNode.getNodeAddress() + ":"); "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.info(result.toString()); log.debug(result.toString());
log.info("Number of reported peers: {}", reportedPeers.size());
} }
} }
@ -372,21 +370,16 @@ public class PeerManager implements ConnectionListener, MessageListener {
reportedPeers.stream() reportedPeers.stream()
.filter(reportedPeer -> reportedPeer.nodeAddress.equals(nodeAddress)) .filter(reportedPeer -> reportedPeer.nodeAddress.equals(nodeAddress))
.findAny() .findAny()
.ifPresent(this::adjustLastActivityDate); .ifPresent(ReportedPeer::penalizeLastActivityDate);
persistedPeers.stream() persistedPeers.stream()
.filter(reportedPeer -> reportedPeer.nodeAddress.equals(nodeAddress)) .filter(reportedPeer -> reportedPeer.nodeAddress.equals(nodeAddress))
.findAny() .findAny()
.ifPresent(reportedPeer -> { .ifPresent(reportedPeer -> {
adjustLastActivityDate(reportedPeer); reportedPeer.penalizeLastActivityDate();
dbStorage.queueUpForSave(persistedPeers, 5000); dbStorage.queueUpForSave(persistedPeers, 5000);
}); });
}
private void adjustLastActivityDate(ReportedPeer reportedPeer) { removeTooOldReportedPeers();
long now = new Date().getTime();
long diff = now - reportedPeer.lastActivityDate.getTime();
long reduced = now - diff * 2;
reportedPeer.setLastActivityDate(new Date(reduced));
} }
public Set<ReportedPeer> getConnectedAndReportedPeers() { public Set<ReportedPeer> getConnectedAndReportedPeers() {

View file

@ -18,12 +18,13 @@ public class ReportedPeer implements Serializable {
this.lastActivityDate = lastActivityDate; this.lastActivityDate = lastActivityDate;
} }
public ReportedPeer(NodeAddress nodeAddress) { public void penalizeLastActivityDate() {
this(nodeAddress, null); if (lastActivityDate != null) {
long now = new Date().getTime();
long diff = Math.max(24 * 60 * 60 * 1000, now - lastActivityDate.getTime());
long reduced = now - diff * 2;
lastActivityDate = new Date(reduced);
} }
public void setLastActivityDate(Date lastActivityDate) {
this.lastActivityDate = lastActivityDate;
} }
// We don't use the lastActivityDate for identity // We don't use the lastActivityDate for identity

View file

@ -233,6 +233,7 @@ public class RequestDataManager implements MessageListener {
!peerManager.isSelf(e)) !peerManager.isSelf(e))
.collect(Collectors.toList()) .collect(Collectors.toList())
.stream() .stream()
.filter(e -> e.lastActivityDate != null)
.sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate)) .sorted((o1, o2) -> o2.lastActivityDate.compareTo(o1.lastActivityDate))
.map(e -> e.nodeAddress) .map(e -> e.nodeAddress)
.collect(Collectors.toList()); .collect(Collectors.toList());

View file

@ -161,7 +161,8 @@ public class P2PDataStorage implements MessageListener {
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n"); StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
sb.append("Data set after addProtectedExpirableData (truncated)"); sb.append("Data set after addProtectedExpirableData (truncated)");
map.values().stream().forEach(e -> sb.append("\n").append(e.toString().substring(0, 40)).append("...\n")); map.values().stream().forEach(e -> sb.append("\n").append(e.toString()
.substring(0, Math.min(50, e.toString().length()))).append("...\n"));
sb.append("\n------------------------------------------------------------\n"); sb.append("\n------------------------------------------------------------\n");
log.trace(sb.toString()); log.trace(sb.toString());
log.info("Data set after addProtectedExpirableData: size=" + map.values().size()); log.info("Data set after addProtectedExpirableData: size=" + map.values().size());
@ -280,7 +281,8 @@ public class P2PDataStorage implements MessageListener {
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n" + StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n" +
"Data set after removeProtectedExpirableData: (truncated)"); "Data set after removeProtectedExpirableData: (truncated)");
map.values().stream().forEach(e -> sb.append("\n").append(e.toString().substring(0, 40)).append("...\n")); map.values().stream().forEach(e -> sb.append("\n").append(e.toString()
.substring(0, Math.min(50, e.toString().length()))).append("...\n"));
sb.append("\n------------------------------------------------------------\n"); sb.append("\n------------------------------------------------------------\n");
log.trace(sb.toString()); log.trace(sb.toString());
log.info("Data set after addProtectedExpirableData: size=" + map.values().size()); log.info("Data set after addProtectedExpirableData: size=" + map.values().size());