Search in sources :

Example 1 with StoragePayload

use of io.bitsquare.p2p.storage.payload.StoragePayload in project bitsquare by bitsquare.

the class TradeStatisticsManager method init.

private void init(P2PService p2PService) {
    if (dumpStatistics) {
        this.statisticsJsonStorage.initWithFileName("trade_statistics.json");
        this.fiatCurrencyListJsonStorage.initWithFileName("fiat_currency_list.json");
        ArrayList<CurrencyTuple> fiatCurrencyList = new ArrayList<>(CurrencyUtil.getAllSortedFiatCurrencies().stream().map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8)).collect(Collectors.toList()));
        fiatCurrencyListJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(fiatCurrencyList)), 2000);
        this.cryptoCurrencyListJsonStorage.initWithFileName("crypto_currency_list.json");
        ArrayList<CurrencyTuple> cryptoCurrencyList = new ArrayList<>(CurrencyUtil.getAllSortedCryptoCurrencies().stream().map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8)).collect(Collectors.toList()));
        cryptoCurrencyList.add(0, new CurrencyTuple("BTC", "Bitcoin", 8));
        cryptoCurrencyListJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(cryptoCurrencyList)), 2000);
    }
    HashSet<TradeStatistics> persisted = statisticsStorage.initAndGetPersistedWithFileName("TradeStatistics");
    if (persisted != null)
        persisted.stream().forEach(e -> add(e, false));
    p2PService.addHashSetChangedListener(new HashMapChangedListener() {

        @Override
        public void onAdded(ProtectedStorageEntry data) {
            final StoragePayload storagePayload = data.getStoragePayload();
            if (storagePayload instanceof TradeStatistics)
                add((TradeStatistics) storagePayload, true);
        }

        @Override
        public void onRemoved(ProtectedStorageEntry data) {
        // We don't remove items
        }
    });
    // At startup the P2PDataStorage inits earlier, otherwise we ge the listener called.
    p2PService.getP2PDataStorage().getMap().values().forEach(e -> {
        final StoragePayload storagePayload = e.getStoragePayload();
        if (storagePayload instanceof TradeStatistics)
            add((TradeStatistics) storagePayload, false);
    });
}
Also used : CurrencyTuple(io.bitsquare.locale.CurrencyTuple) ObservableSet(javafx.collections.ObservableSet) Utilities(io.bitsquare.common.util.Utilities) Logger(org.slf4j.Logger) P2PService(io.bitsquare.p2p.P2PService) Inject(com.google.inject.Inject) ProtectedStorageEntry(io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry) LoggerFactory(org.slf4j.LoggerFactory) FXCollections(javafx.collections.FXCollections) HashMapChangedListener(io.bitsquare.p2p.storage.HashMapChangedListener) StoragePayload(io.bitsquare.p2p.storage.payload.StoragePayload) Collectors(java.util.stream.Collectors) AppOptionKeys(io.bitsquare.app.AppOptionKeys) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) List(java.util.List) PlainTextWrapper(io.bitsquare.storage.PlainTextWrapper) Named(com.google.inject.name.Named) Storage(io.bitsquare.storage.Storage) CurrencyUtil(io.bitsquare.locale.CurrencyUtil) CurrencyTuple(io.bitsquare.locale.CurrencyTuple) HashMapChangedListener(io.bitsquare.p2p.storage.HashMapChangedListener) ArrayList(java.util.ArrayList) PlainTextWrapper(io.bitsquare.storage.PlainTextWrapper) StoragePayload(io.bitsquare.p2p.storage.payload.StoragePayload) ProtectedStorageEntry(io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry)

Example 2 with StoragePayload

use of io.bitsquare.p2p.storage.payload.StoragePayload in project bitsquare by bitsquare.

the class RequestDataHandler method onMessage.

///////////////////////////////////////////////////////////////////////////////////////////
// MessageListener implementation
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onMessage(Message message, Connection connection) {
    if (connection.getPeersNodeAddressOptional().isPresent() && connection.getPeersNodeAddressOptional().get().equals(peersNodeAddress)) {
        if (message instanceof GetDataResponse) {
            Log.traceCall(message.toString() + "\n\tconnection=" + connection);
            if (!stopped) {
                GetDataResponse getDataResponse = (GetDataResponse) message;
                Map<String, Set<StoragePayload>> payloadByClassName = new HashMap<>();
                final HashSet<ProtectedStorageEntry> dataSet = getDataResponse.dataSet;
                dataSet.stream().forEach(e -> {
                    final StoragePayload storagePayload = e.getStoragePayload();
                    String className = storagePayload.getClass().getSimpleName();
                    if (!payloadByClassName.containsKey(className))
                        payloadByClassName.put(className, new HashSet<>());
                    payloadByClassName.get(className).add(storagePayload);
                });
                StringBuilder sb = new StringBuilder("Received data size: ").append(dataSet.size()).append(", data items: ");
                payloadByClassName.entrySet().stream().forEach(e -> sb.append(e.getValue().size()).append(" items of ").append(e.getKey()).append("; "));
                log.info(sb.toString());
                if (getDataResponse.requestNonce == nonce) {
                    stopTimeoutTimer();
                    checkArgument(connection.getPeersNodeAddressOptional().isPresent(), "RequestDataHandler.onMessage: connection.getPeersNodeAddressOptional() must be present " + "at that moment");
                    final NodeAddress sender = connection.getPeersNodeAddressOptional().get();
                    List<ProtectedStorageEntry> processDelayedItems = new ArrayList<>();
                    dataSet.stream().forEach(e -> {
                        if (e.getStoragePayload() instanceof LazyProcessedStoragePayload)
                            processDelayedItems.add(e);
                        else {
                            dataStorage.add(e, sender, null, false, false);
                        }
                    });
                    // We process the LazyProcessedStoragePayload items (TradeStatistics) in batches with a delay in between.
                    // We want avoid that the UI get stuck when processing many entries.
                    // The dataStorage.add call is a bit expensive as sig checks is done there.
                    // Using a background thread might be an alternative but it would require much more effort and 
                    // it would also decrease user experience if the app gets under heavy load (like at startup with wallet sync).
                    // Beside that we mitigated the problem already as we will not get the whole TradeStatistics as we 
                    // pass the excludeKeys and we pack the latest data dump 
                    // into the resources, so a new user do not need to request all data.
                    // In future we will probably limit by date or load on demand from user intent to not get too much data.
                    // We split the list into sub lists with max 50 items and delay each batch with 200 ms.
                    int size = processDelayedItems.size();
                    int chunkSize = 50;
                    int chunks = 1 + size / chunkSize;
                    int startIndex = 0;
                    for (int i = 0; i < chunks && startIndex < size; i++, startIndex += chunkSize) {
                        long delay = (i + 1) * 200;
                        int endIndex = Math.min(size, startIndex + chunkSize);
                        List<ProtectedStorageEntry> subList = processDelayedItems.subList(startIndex, endIndex);
                        UserThread.runAfter(() -> {
                            subList.stream().forEach(protectedStorageEntry -> dataStorage.add(protectedStorageEntry, sender, null, false, false));
                        }, delay, TimeUnit.MILLISECONDS);
                    }
                    cleanup();
                    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\t" + "We drop that message. nonce={} / requestNonce={}", nonce, getDataResponse.requestNonce);
                }
            } else {
                log.warn("We have stopped already. We ignore that onDataRequest call.");
            }
        }
    } else {
        log.trace("We got a message from another connection and ignore it.");
    }
}
Also used : LazyProcessedStoragePayload(io.bitsquare.p2p.storage.payload.LazyProcessedStoragePayload) GetDataResponse(io.bitsquare.p2p.peers.getdata.messages.GetDataResponse) NodeAddress(io.bitsquare.p2p.NodeAddress) PersistedStoragePayload(io.bitsquare.p2p.storage.payload.PersistedStoragePayload) StoragePayload(io.bitsquare.p2p.storage.payload.StoragePayload) LazyProcessedStoragePayload(io.bitsquare.p2p.storage.payload.LazyProcessedStoragePayload) ProtectedStorageEntry(io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry)

Example 3 with StoragePayload

use of io.bitsquare.p2p.storage.payload.StoragePayload in project bitsquare by bitsquare.

the class Connection method isCapabilitySupported.

public boolean isCapabilitySupported(Message message) {
    if (message instanceof AddDataMessage) {
        final StoragePayload storagePayload = (((AddDataMessage) message).protectedStorageEntry).getStoragePayload();
        if (storagePayload instanceof CapabilityRequiringPayload) {
            final List<Integer> requiredCapabilities = ((CapabilityRequiringPayload) storagePayload).getRequiredCapabilities();
            final List<Integer> supportedCapabilities = sharedModel.getSupportedCapabilities();
            if (supportedCapabilities != null) {
                for (int messageCapability : requiredCapabilities) {
                    for (int connectionCapability : supportedCapabilities) {
                        if (messageCapability == connectionCapability)
                            return true;
                    }
                }
                log.debug("We do not send the message to the peer because he does not support the required capability for that message type.\n" + "Required capabilities is: " + requiredCapabilities.toString() + "\n" + "Supported capabilities is: " + supportedCapabilities.toString() + "\n" + "connection: " + this.toString() + "\n" + "storagePayload is: " + Utilities.toTruncatedString(storagePayload));
                return false;
            } else {
                log.debug("We do not send the message to the peer because he uses an old version which does not support capabilities.\n" + "Required capabilities is: " + requiredCapabilities.toString() + "\n" + "connection: " + this.toString() + "\n" + "storagePayload is: " + Utilities.toTruncatedString(storagePayload));
                return false;
            }
        } else {
            return true;
        }
    } else {
        return true;
    }
}
Also used : AddDataMessage(io.bitsquare.p2p.storage.messages.AddDataMessage) CapabilityRequiringPayload(io.bitsquare.p2p.storage.payload.CapabilityRequiringPayload) StoragePayload(io.bitsquare.p2p.storage.payload.StoragePayload)

Example 4 with StoragePayload

use of io.bitsquare.p2p.storage.payload.StoragePayload in project bitsquare by bitsquare.

the class GetDataRequestHandler method handle.

///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void handle(GetDataRequest getDataRequest, final Connection connection) {
    Log.traceCall(getDataRequest + "\n\tconnection=" + connection);
    final HashSet<ProtectedStorageEntry> filteredDataSet = new HashSet<>();
    final Set<Integer> lookupSet = new HashSet<>();
    Set<P2PDataStorage.ByteArray> excludedItems = getDataRequest.getExcludedKeys() != null ? getDataRequest.getExcludedKeys().stream().map(P2PDataStorage.ByteArray::new).collect(Collectors.toSet()) : new HashSet<>();
    for (ProtectedStorageEntry protectedStorageEntry : dataStorage.getFilteredValues(excludedItems)) {
        final StoragePayload storagePayload = protectedStorageEntry.getStoragePayload();
        boolean doAdd = false;
        if (storagePayload instanceof CapabilityRequiringPayload) {
            final List<Integer> requiredCapabilities = ((CapabilityRequiringPayload) storagePayload).getRequiredCapabilities();
            final List<Integer> supportedCapabilities = connection.getSupportedCapabilities();
            if (supportedCapabilities != null) {
                for (int messageCapability : requiredCapabilities) {
                    for (int connectionCapability : supportedCapabilities) {
                        if (messageCapability == connectionCapability) {
                            doAdd = true;
                            break;
                        }
                    }
                }
                if (!doAdd)
                    log.debug("We do not send the message to the peer because he does not support the required capability for that message type.\n" + "Required capabilities is: " + requiredCapabilities.toString() + "\n" + "Supported capabilities is: " + supportedCapabilities.toString() + "\n" + "storagePayload is: " + Utilities.toTruncatedString(storagePayload));
            } else {
                log.debug("We do not send the message to the peer because he uses an old version which does not support capabilities.\n" + "Required capabilities is: " + requiredCapabilities.toString() + "\n" + "storagePayload is: " + Utilities.toTruncatedString(storagePayload));
            }
        } else {
            doAdd = true;
        }
        if (doAdd) {
            // We have TradeStatistic data of both traders but we only send 1 item, 
            // so we use lookupSet as for a fast lookup. Using filteredDataSet would require a loop as it stores 
            // protectedStorageEntry not storagePayload. protectedStorageEntry is different for both traders but storagePayload not, 
            // as we ignore the pubKey and data there in the hashCode method.
            boolean notContained = lookupSet.add(storagePayload.hashCode());
            if (notContained)
                filteredDataSet.add(protectedStorageEntry);
        }
    }
    GetDataResponse getDataResponse = new GetDataResponse(filteredDataSet, getDataRequest.getNonce(), getDataRequest instanceof GetUpdatedDataRequest);
    if (timeoutTimer == null) {
        timeoutTimer = UserThread.runAfter(() -> {
            // setup before sending to avoid race conditions
            String errorMessage = "A timeout occurred for getDataResponse:" + getDataResponse + " on connection:" + connection;
            handleFault(errorMessage, CloseConnectionReason.SEND_MSG_TIMEOUT, connection);
        }, TIME_OUT_SEC, TimeUnit.SECONDS);
    }
    SettableFuture<Connection> future = networkNode.sendMessage(connection, getDataResponse);
    Futures.addCallback(future, new FutureCallback<Connection>() {

        @Override
        public void onSuccess(Connection connection) {
            if (!stopped) {
                log.trace("Send DataResponse to {} succeeded. getDataResponse={}", connection.getPeersNodeAddressOptional(), getDataResponse);
                cleanup();
                listener.onComplete();
            } else {
                log.trace("We have stopped already. We ignore that networkNode.sendMessage.onSuccess call.");
            }
        }

        @Override
        public void onFailure(@NotNull Throwable throwable) {
            if (!stopped) {
                String errorMessage = "Sending getDataRequest to " + connection + " failed. That is expected if the peer is offline. getDataResponse=" + getDataResponse + "." + "Exception: " + throwable.getMessage();
                handleFault(errorMessage, CloseConnectionReason.SEND_MSG_FAILURE, connection);
            } else {
                log.trace("We have stopped already. We ignore that networkNode.sendMessage.onFailure call.");
            }
        }
    });
}
Also used : Connection(io.bitsquare.p2p.network.Connection) CapabilityRequiringPayload(io.bitsquare.p2p.storage.payload.CapabilityRequiringPayload) GetDataResponse(io.bitsquare.p2p.peers.getdata.messages.GetDataResponse) GetUpdatedDataRequest(io.bitsquare.p2p.peers.getdata.messages.GetUpdatedDataRequest) StoragePayload(io.bitsquare.p2p.storage.payload.StoragePayload) HashSet(java.util.HashSet) ProtectedStorageEntry(io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry)

Aggregations

StoragePayload (io.bitsquare.p2p.storage.payload.StoragePayload)4 ProtectedStorageEntry (io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry)3 GetDataResponse (io.bitsquare.p2p.peers.getdata.messages.GetDataResponse)2 CapabilityRequiringPayload (io.bitsquare.p2p.storage.payload.CapabilityRequiringPayload)2 HashSet (java.util.HashSet)2 Inject (com.google.inject.Inject)1 Named (com.google.inject.name.Named)1 AppOptionKeys (io.bitsquare.app.AppOptionKeys)1 Utilities (io.bitsquare.common.util.Utilities)1 CurrencyTuple (io.bitsquare.locale.CurrencyTuple)1 CurrencyUtil (io.bitsquare.locale.CurrencyUtil)1 NodeAddress (io.bitsquare.p2p.NodeAddress)1 P2PService (io.bitsquare.p2p.P2PService)1 Connection (io.bitsquare.p2p.network.Connection)1 GetUpdatedDataRequest (io.bitsquare.p2p.peers.getdata.messages.GetUpdatedDataRequest)1 HashMapChangedListener (io.bitsquare.p2p.storage.HashMapChangedListener)1 AddDataMessage (io.bitsquare.p2p.storage.messages.AddDataMessage)1 LazyProcessedStoragePayload (io.bitsquare.p2p.storage.payload.LazyProcessedStoragePayload)1 PersistedStoragePayload (io.bitsquare.p2p.storage.payload.PersistedStoragePayload)1 PlainTextWrapper (io.bitsquare.storage.PlainTextWrapper)1