mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-07-25 16:05:28 -04:00
Handle offer removal on disconnect, cleanup
This commit is contained in:
parent
db363fac48
commit
17c780639f
69 changed files with 377 additions and 367 deletions
|
@ -60,6 +60,7 @@ public abstract class AppModule extends AbstractModule {
|
||||||
*
|
*
|
||||||
* @param injector the Injector originally initialized with this module
|
* @param injector the Injector originally initialized with this module
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
protected void doClose(Injector injector) {
|
protected void doClose(Injector injector) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Log {
|
public class Log {
|
||||||
public static boolean PRINT_TRACE_METHOD = true;
|
private static boolean PRINT_TRACE_METHOD = true;
|
||||||
private static SizeBasedTriggeringPolicy triggeringPolicy;
|
private static SizeBasedTriggeringPolicy triggeringPolicy;
|
||||||
private static Logger logbackLogger;
|
private static Logger logbackLogger;
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ import io.bitsquare.app.Version;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// Util for comparing byte arrays
|
||||||
public class ByteArray implements Serializable {
|
public class ByteArray implements Serializable {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
||||||
|
|
||||||
public final byte[] bytes;
|
private final byte[] bytes;
|
||||||
|
|
||||||
public ByteArray(byte[] bytes) {
|
public ByteArray(byte[] bytes) {
|
||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
|
|
|
@ -55,6 +55,7 @@ public class UserThread {
|
||||||
return UserThread.runAfterRandomDelay(runnable, minDelayInSec, maxDelayInSec, TimeUnit.SECONDS);
|
return UserThread.runAfterRandomDelay(runnable, minDelayInSec, maxDelayInSec, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public static Timer runAfterRandomDelay(Runnable runnable, long minDelay, long maxDelay, TimeUnit timeUnit) {
|
public static Timer runAfterRandomDelay(Runnable runnable, long minDelay, long maxDelay, TimeUnit timeUnit) {
|
||||||
return UserThread.runAfter(runnable, new Random().nextInt((int) (maxDelay - minDelay)) + minDelay, timeUnit);
|
return UserThread.runAfter(runnable, new Random().nextInt((int) (maxDelay - minDelay)) + minDelay, timeUnit);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +71,7 @@ public class UserThread {
|
||||||
public void run() {
|
public void run() {
|
||||||
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
|
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
|
||||||
try {
|
try {
|
||||||
UserThread.execute(() -> runnable.run());
|
UserThread.execute(runnable::run);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
log.error("Executing timerTask failed. " + t.getMessage());
|
log.error("Executing timerTask failed. " + t.getMessage());
|
||||||
|
|
|
@ -40,12 +40,12 @@ public class Encryption {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
|
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
|
||||||
|
|
||||||
public static final String ASYM_KEY_ALGO = "RSA"; // RSA/NONE/OAEPWithSHA256AndMGF1Padding
|
public static final String ASYM_KEY_ALGO = "RSA"; // RSA/NONE/OAEPWithSHA256AndMGF1Padding
|
||||||
public static final String ASYM_CIPHER = "RSA";
|
private static final String ASYM_CIPHER = "RSA";
|
||||||
|
|
||||||
public static final String SYM_KEY_ALGO = "AES"; // AES/CTR/NoPadding
|
private static final String SYM_KEY_ALGO = "AES"; // AES/CTR/NoPadding
|
||||||
public static final String SYM_CIPHER = "AES";
|
private static final String SYM_CIPHER = "AES";
|
||||||
|
|
||||||
public static final String HMAC = "HmacSHA256";
|
private static final String HMAC = "HmacSHA256";
|
||||||
|
|
||||||
public static KeyPair generateKeyPair() {
|
public static KeyPair generateKeyPair() {
|
||||||
long ts = System.currentTimeMillis();
|
long ts = System.currentTimeMillis();
|
||||||
|
@ -66,7 +66,7 @@ public class Encryption {
|
||||||
// Symmetric
|
// Symmetric
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public static byte[] encrypt(byte[] payload, SecretKey secretKey) throws CryptoException {
|
private static byte[] encrypt(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
@ -77,7 +77,7 @@ public class Encryption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decrypt(byte[] encryptedPayload, SecretKey secretKey) throws CryptoException {
|
private static byte[] decrypt(byte[] encryptedPayload, SecretKey secretKey) throws CryptoException {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
Cipher cipher = Cipher.getInstance(SYM_CIPHER, "BC");
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||||
|
@ -123,7 +123,7 @@ public class Encryption {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static boolean verifyHmac(byte[] message, byte[] hmac, SecretKey secretKey) throws CryptoException {
|
private static boolean verifyHmac(byte[] message, byte[] hmac, SecretKey secretKey) {
|
||||||
try {
|
try {
|
||||||
byte[] hmacTest = getHmac(message, secretKey);
|
byte[] hmacTest = getHmac(message, secretKey);
|
||||||
return Arrays.equals(hmacTest, hmac);
|
return Arrays.equals(hmacTest, hmac);
|
||||||
|
@ -144,15 +144,15 @@ public class Encryption {
|
||||||
// Symmetric with Hmac
|
// Symmetric with Hmac
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public static byte[] encryptPayloadWithHmac(Serializable object, SecretKey secretKey) throws CryptoException {
|
private static byte[] encryptPayloadWithHmac(Serializable object, SecretKey secretKey) throws CryptoException {
|
||||||
return encryptPayloadWithHmac(Utilities.serialize(object), secretKey);
|
return encryptPayloadWithHmac(Utilities.serialize(object), secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] encryptPayloadWithHmac(byte[] payload, SecretKey secretKey) throws CryptoException {
|
private static byte[] encryptPayloadWithHmac(byte[] payload, SecretKey secretKey) throws CryptoException {
|
||||||
return encrypt(getPayloadWithHmac(payload, secretKey), secretKey);
|
return encrypt(getPayloadWithHmac(payload, secretKey), secretKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decryptPayloadWithHmac(byte[] encryptedPayloadWithHmac, SecretKey secretKey) throws CryptoException {
|
private static byte[] decryptPayloadWithHmac(byte[] encryptedPayloadWithHmac, SecretKey secretKey) throws CryptoException {
|
||||||
byte[] payloadWithHmac = decrypt(encryptedPayloadWithHmac, secretKey);
|
byte[] payloadWithHmac = decrypt(encryptedPayloadWithHmac, secretKey);
|
||||||
String payloadWithHmacAsHex = Hex.toHexString(payloadWithHmac);
|
String payloadWithHmacAsHex = Hex.toHexString(payloadWithHmac);
|
||||||
// first part is raw message
|
// first part is raw message
|
||||||
|
@ -173,7 +173,7 @@ public class Encryption {
|
||||||
// Asymmetric
|
// Asymmetric
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public static byte[] encrypt(byte[] payload, PublicKey publicKey) throws CryptoException {
|
private static byte[] encrypt(byte[] payload, PublicKey publicKey) throws CryptoException {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||||
|
@ -184,7 +184,7 @@ public class Encryption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decrypt(byte[] encryptedPayload, PrivateKey privateKey) throws CryptoException {
|
private static byte[] decrypt(byte[] encryptedPayload, PrivateKey privateKey) throws CryptoException {
|
||||||
try {
|
try {
|
||||||
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
Cipher cipher = Cipher.getInstance(ASYM_CIPHER, "BC");
|
||||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
|
|
@ -136,7 +136,7 @@ public class KeyStorage {
|
||||||
savePrivateKey(keyRing.getEncryptionKeyPair().getPrivate(), KeyEntry.MSG_ENCRYPTION.getFileName());
|
savePrivateKey(keyRing.getEncryptionKeyPair().getPrivate(), KeyEntry.MSG_ENCRYPTION.getFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void savePrivateKey(PrivateKey privateKey, String name) {
|
private void savePrivateKey(PrivateKey privateKey, String name) {
|
||||||
if (!storageDir.exists())
|
if (!storageDir.exists())
|
||||||
storageDir.mkdir();
|
storageDir.mkdir();
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class Sig {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Sig.class);
|
private static final Logger log = LoggerFactory.getLogger(Sig.class);
|
||||||
|
|
||||||
public static final String KEY_ALGO = "DSA";
|
public static final String KEY_ALGO = "DSA";
|
||||||
public static final String ALGO = "SHA256withDSA";
|
private static final String ALGO = "SHA256withDSA";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -99,7 +99,6 @@ public class Sig {
|
||||||
* @throws SignatureException
|
* @throws SignatureException
|
||||||
*/
|
*/
|
||||||
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws CryptoException {
|
public static boolean verify(PublicKey publicKey, byte[] data, byte[] signature) throws CryptoException {
|
||||||
byte[] sigAsBytes = new byte[0];
|
|
||||||
try {
|
try {
|
||||||
Signature sig = Signature.getInstance(ALGO, "BC");
|
Signature sig = Signature.getInstance(ALGO, "BC");
|
||||||
sig.initVerify(publicKey);
|
sig.initVerify(publicKey);
|
||||||
|
|
|
@ -74,7 +74,7 @@ class DesktopUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
if (!Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
logErr("BORWSE is not supported.");
|
logErr("BROWSE is not supported.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ package io.bitsquare.common.util;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Profiler {
|
class Profiler {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Profiler.class);
|
private static final Logger log = LoggerFactory.getLogger(Profiler.class);
|
||||||
|
|
||||||
public static void printSystemLoad(Logger log) {
|
public static void printSystemLoad(Logger log) {
|
||||||
|
|
|
@ -75,9 +75,7 @@ public class Utilities {
|
||||||
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
|
||||||
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
|
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
|
||||||
executor.allowCoreThreadTimeOut(true);
|
executor.allowCoreThreadTimeOut(true);
|
||||||
executor.setRejectedExecutionHandler((r, e) -> {
|
executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
|
||||||
log.warn("RejectedExecutionHandler called");
|
|
||||||
});
|
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,9 +94,7 @@ public class Utilities {
|
||||||
executor.allowCoreThreadTimeOut(true);
|
executor.allowCoreThreadTimeOut(true);
|
||||||
executor.setMaximumPoolSize(maximumPoolSize);
|
executor.setMaximumPoolSize(maximumPoolSize);
|
||||||
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||||
executor.setRejectedExecutionHandler((r, e) -> {
|
executor.setRejectedExecutionHandler((r, e) -> log.warn("RejectedExecutionHandler called"));
|
||||||
log.warn("RejectedExecutionHandler called");
|
|
||||||
});
|
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +288,9 @@ public class Utilities {
|
||||||
|
|
||||||
public static void deleteDirectory(File file) throws IOException {
|
public static void deleteDirectory(File file) throws IOException {
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
for (File c : file.listFiles())
|
File[] files = file.listFiles();
|
||||||
|
if (files != null)
|
||||||
|
for (File c : files)
|
||||||
deleteDirectory(c);
|
deleteDirectory(c);
|
||||||
}
|
}
|
||||||
if (!file.delete())
|
if (!file.delete())
|
||||||
|
|
|
@ -15,26 +15,8 @@
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright 2013 Google Inc.
|
|
||||||
* Copyright 2014 Andreas Schildbach
|
|
||||||
* <p>
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* <p>
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* <p>
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.storage;
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Utilities;
|
import io.bitsquare.common.util.Utilities;
|
||||||
|
@ -50,13 +32,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Borrowed from BitcoinJ WalletFiles
|
|
||||||
* A class that handles atomic and optionally delayed writing of a file to disk.
|
|
||||||
* It can be useful to delay writing of a file to disk on slow devices.
|
|
||||||
* By coalescing writes and doing serialization
|
|
||||||
* and disk IO on a background thread performance can be improved.
|
|
||||||
*/
|
|
||||||
public class FileManager<T> {
|
public class FileManager<T> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(FileManager.class);
|
private static final Logger log = LoggerFactory.getLogger(FileManager.class);
|
||||||
|
|
||||||
|
@ -166,7 +141,7 @@ public class FileManager<T> {
|
||||||
/**
|
/**
|
||||||
* Shut down auto-saving.
|
* Shut down auto-saving.
|
||||||
*/
|
*/
|
||||||
public void shutDown() {
|
void shutDown() {
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
try {
|
try {
|
||||||
executor.awaitTermination(5, TimeUnit.SECONDS);
|
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
|
|
|
@ -109,7 +109,7 @@ public class Storage<T extends Serializable> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save delayed and on a background thread
|
// Save delayed and on a background thread
|
||||||
public void queueUpForSave(T serializable) {
|
private void queueUpForSave(T serializable) {
|
||||||
if (serializable != null) {
|
if (serializable != null) {
|
||||||
log.trace("save " + fileName);
|
log.trace("save " + fileName);
|
||||||
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
package io.bitsquare.alert;
|
package io.bitsquare.alert;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.storage.data.StorageMessage;
|
import io.bitsquare.p2p.storage.messages.StorageMessage;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
|
@ -20,7 +20,7 @@ package io.bitsquare.alert;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import io.bitsquare.common.crypto.KeyRing;
|
import io.bitsquare.common.crypto.KeyRing;
|
||||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
@ -30,7 +30,6 @@ import org.bitcoinj.core.Utils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
|
||||||
|
@ -61,20 +60,18 @@ public class AlertManager {
|
||||||
|
|
||||||
alertService.addHashSetChangedListener(new HashMapChangedListener() {
|
alertService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAdded(ProtectedData entry) {
|
public void onAdded(ProtectedData data) {
|
||||||
Serializable data = entry.expirableMessage;
|
if (data.expirableMessage instanceof Alert) {
|
||||||
if (data instanceof Alert) {
|
Alert alert = (Alert) data.expirableMessage;
|
||||||
Alert alert = (Alert) data;
|
|
||||||
if (verifySignature(alert))
|
if (verifySignature(alert))
|
||||||
alertMessageProperty.set(alert);
|
alertMessageProperty.set(alert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoved(ProtectedData entry) {
|
public void onRemoved(ProtectedData data) {
|
||||||
Serializable data = entry.expirableMessage;
|
if (data.expirableMessage instanceof Alert) {
|
||||||
if (data instanceof Alert) {
|
Alert alert = (Alert) data.expirableMessage;
|
||||||
Alert alert = (Alert) data;
|
|
||||||
if (verifySignature(alert))
|
if (verifySignature(alert))
|
||||||
alertMessageProperty.set(null);
|
alertMessageProperty.set(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ package io.bitsquare.arbitration;
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.common.crypto.PubKeyRing;
|
import io.bitsquare.common.crypto.PubKeyRing;
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
import io.bitsquare.p2p.storage.data.StorageMessage;
|
import io.bitsquare.p2p.storage.messages.StorageMessage;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
|
@ -30,7 +30,7 @@ import io.bitsquare.p2p.BootstrapListener;
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
import io.bitsquare.p2p.P2PService;
|
import io.bitsquare.p2p.P2PService;
|
||||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableMap;
|
import javafx.collections.ObservableMap;
|
||||||
|
@ -101,12 +101,12 @@ public class ArbitratorManager {
|
||||||
|
|
||||||
arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
|
arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAdded(ProtectedData entry) {
|
public void onAdded(ProtectedData data) {
|
||||||
applyArbitrators();
|
applyArbitrators();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoved(ProtectedData entry) {
|
public void onRemoved(ProtectedData data) {
|
||||||
applyArbitrators();
|
applyArbitrators();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -137,7 +137,7 @@ public class ArbitratorManager {
|
||||||
// re-publish periodically
|
// re-publish periodically
|
||||||
republishArbitratorExecutor = Utilities.getScheduledThreadPoolExecutor("republishArbitrator", 1, 5, 5);
|
republishArbitratorExecutor = Utilities.getScheduledThreadPoolExecutor("republishArbitrator", 1, 5, 5);
|
||||||
long delay = Arbitrator.TTL / 2;
|
long delay = Arbitrator.TTL / 2;
|
||||||
republishArbitratorExecutor.scheduleAtFixedRate(() -> republishArbitrator(), delay, delay, TimeUnit.MILLISECONDS);
|
republishArbitratorExecutor.scheduleAtFixedRate(this::republishArbitrator, delay, delay, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyArbitrators();
|
applyArbitrators();
|
||||||
|
@ -150,7 +150,7 @@ public class ArbitratorManager {
|
||||||
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
|
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
|
||||||
if (registeredArbitrator != null) {
|
if (registeredArbitrator != null) {
|
||||||
addArbitrator(registeredArbitrator,
|
addArbitrator(registeredArbitrator,
|
||||||
() -> applyArbitrators(),
|
this::applyArbitrators,
|
||||||
log::error
|
log::error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ public class ArbitratorManager {
|
||||||
|
|
||||||
public void applyArbitrators() {
|
public void applyArbitrators() {
|
||||||
Map<NodeAddress, Arbitrator> map = arbitratorService.getArbitrators();
|
Map<NodeAddress, Arbitrator> map = arbitratorService.getArbitrators();
|
||||||
log.trace("Arbitrators . size=" + (map.values() != null ? map.values().size() : "0"));
|
log.trace("Arbitrators . size=" + map.values().size());
|
||||||
arbitratorsObservableMap.clear();
|
arbitratorsObservableMap.clear();
|
||||||
Map<NodeAddress, Arbitrator> filtered = map.values().stream()
|
Map<NodeAddress, Arbitrator> filtered = map.values().stream()
|
||||||
.filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
|
.filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
|
||||||
|
@ -176,8 +176,8 @@ public class ArbitratorManager {
|
||||||
// if we don't have any arbitrator anymore we set all matching
|
// if we don't have any arbitrator anymore we set all matching
|
||||||
if (user.getAcceptedArbitrators().isEmpty()) {
|
if (user.getAcceptedArbitrators().isEmpty()) {
|
||||||
arbitratorsObservableMap.values().stream()
|
arbitratorsObservableMap.values().stream()
|
||||||
.filter(arbitrator -> user.hasMatchingLanguage(arbitrator))
|
.filter(user::hasMatchingLanguage)
|
||||||
.forEach(arbitrator -> user.addAcceptedArbitrator(arbitrator));
|
.forEach(user::addAcceptedArbitrator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ public class ArbitratorManager {
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
|
|
||||||
if (arbitratorsObservableMap.size() > 0)
|
if (arbitratorsObservableMap.size() > 0)
|
||||||
UserThread.runAfter(() -> applyArbitrators(), 100, TimeUnit.MILLISECONDS);
|
UserThread.runAfter(this::applyArbitrators, 100, TimeUnit.MILLISECONDS);
|
||||||
},
|
},
|
||||||
errorMessageHandler::handleErrorMessage);
|
errorMessageHandler::handleErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,8 @@ public class ArbitratorService {
|
||||||
|
|
||||||
public Map<NodeAddress, Arbitrator> getArbitrators() {
|
public Map<NodeAddress, Arbitrator> getArbitrators() {
|
||||||
Set<Arbitrator> arbitratorSet = p2PService.getDataMap().values().stream()
|
Set<Arbitrator> arbitratorSet = p2PService.getDataMap().values().stream()
|
||||||
.filter(e -> e.expirableMessage instanceof Arbitrator)
|
.filter(data -> data.expirableMessage instanceof Arbitrator)
|
||||||
.map(e -> (Arbitrator) e.expirableMessage)
|
.map(data -> (Arbitrator) data.expirableMessage)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
Map<NodeAddress, Arbitrator> map = new HashMap<>();
|
Map<NodeAddress, Arbitrator> map = new HashMap<>();
|
||||||
|
|
|
@ -575,7 +575,7 @@ public class DisputeManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isArbitrator(DisputeResult disputeResult) {
|
private boolean isArbitrator(DisputeResult disputeResult) {
|
||||||
return walletService.getArbitratorAddressEntry().getAddressString().equals(disputeResult.getArbitratorAddressAsString());
|
return disputeResult.getArbitratorAddressAsString().equals(walletService.getArbitratorAddressEntry().getAddressString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
private static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
||||||
Collections.sort(outputs, (a, b) -> {
|
Collections.sort(outputs, (a, b) -> {
|
||||||
int depth1 = a.getParentTransactionDepthInBlocks();
|
int depth1 = a.getParentTransactionDepthInBlocks();
|
||||||
int depth2 = b.getParentTransactionDepthInBlocks();
|
int depth2 = b.getParentTransactionDepthInBlocks();
|
||||||
|
@ -92,7 +92,7 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||||
/**
|
/**
|
||||||
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
||||||
*/
|
*/
|
||||||
protected boolean shouldSelect(Transaction tx) {
|
private boolean shouldSelect(Transaction tx) {
|
||||||
return isInBlockChainOrPending(tx);
|
return isInBlockChainOrPending(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import io.bitsquare.btc.exceptions.TransactionVerificationException;
|
||||||
import io.bitsquare.btc.exceptions.WalletException;
|
import io.bitsquare.btc.exceptions.WalletException;
|
||||||
import io.bitsquare.user.Preferences;
|
import io.bitsquare.user.Preferences;
|
||||||
import org.bitcoinj.core.*;
|
import org.bitcoinj.core.*;
|
||||||
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
import org.bitcoinj.crypto.TransactionSignature;
|
import org.bitcoinj.crypto.TransactionSignature;
|
||||||
import org.bitcoinj.kits.WalletAppKit;
|
import org.bitcoinj.kits.WalletAppKit;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
|
@ -161,6 +162,7 @@ public class TradeWalletService {
|
||||||
// To be discussed if that introduce any privacy issues.
|
// To be discussed if that introduce any privacy issues.
|
||||||
sendRequest.changeAddress = addressEntry.getAddress();
|
sendRequest.changeAddress = addressEntry.getAddress();
|
||||||
|
|
||||||
|
checkNotNull(wallet, "Wallet must not be null");
|
||||||
wallet.completeTx(sendRequest);
|
wallet.completeTx(sendRequest);
|
||||||
printTxWithInputs("tradingFeeTx", tradingFeeTx);
|
printTxWithInputs("tradingFeeTx", tradingFeeTx);
|
||||||
|
|
||||||
|
@ -248,8 +250,9 @@ public class TradeWalletService {
|
||||||
String changeOutputAddress = null;
|
String changeOutputAddress = null;
|
||||||
if (changeOutput != null) {
|
if (changeOutput != null) {
|
||||||
changeOutputValue = changeOutput.getValue().getValue();
|
changeOutputValue = changeOutput.getValue().getValue();
|
||||||
checkNotNull(changeOutput.getAddressFromP2PKHScript(params), "changeOutput.getAddressFromP2PKHScript(params) must not be null");
|
Address addressFromP2PKHScript = changeOutput.getAddressFromP2PKHScript(params);
|
||||||
changeOutputAddress = changeOutput.getAddressFromP2PKHScript(params).toString();
|
checkNotNull(addressFromP2PKHScript, "changeOutput.getAddressFromP2PKHScript(params) must not be null");
|
||||||
|
changeOutputAddress = addressFromP2PKHScript.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new InputsAndChangeOutput(rawInputList, changeOutputValue, changeOutputAddress);
|
return new InputsAndChangeOutput(rawInputList, changeOutputValue, changeOutputAddress);
|
||||||
|
@ -292,7 +295,7 @@ public class TradeWalletService {
|
||||||
log.trace("msOutputAmount " + msOutputAmount.toFriendlyString());
|
log.trace("msOutputAmount " + msOutputAmount.toFriendlyString());
|
||||||
log.trace("takerRawInputs " + takerRawInputs.toString());
|
log.trace("takerRawInputs " + takerRawInputs.toString());
|
||||||
log.trace("takerChangeOutputValue " + takerChangeOutputValue);
|
log.trace("takerChangeOutputValue " + takerChangeOutputValue);
|
||||||
log.trace("takerChangeAddressString " + takerChangeAddressString != null ? takerChangeAddressString : "");
|
log.trace("takerChangeAddressString " + takerChangeAddressString);
|
||||||
log.trace("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
|
log.trace("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
|
||||||
log.trace("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
|
log.trace("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
|
||||||
log.trace("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
|
log.trace("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
|
||||||
|
@ -499,6 +502,7 @@ public class TradeWalletService {
|
||||||
printTxWithInputs("depositTx", depositTx);
|
printTxWithInputs("depositTx", depositTx);
|
||||||
|
|
||||||
// Broadcast depositTx
|
// Broadcast depositTx
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(depositTx).future();
|
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(depositTx).future();
|
||||||
Futures.addCallback(broadcastComplete, callback);
|
Futures.addCallback(broadcastComplete, callback);
|
||||||
}
|
}
|
||||||
|
@ -552,7 +556,9 @@ public class TradeWalletService {
|
||||||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||||
// MS output from prev. tx is index 0
|
// MS output from prev. tx is index 0
|
||||||
Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||||
ECKey.ECDSASignature sellerSignature = sellerAddressEntry.getKeyPair().sign(sigHash, aesKey).toCanonicalised();
|
DeterministicKey keyPair = sellerAddressEntry.getKeyPair();
|
||||||
|
checkNotNull(keyPair);
|
||||||
|
ECKey.ECDSASignature sellerSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||||
|
|
||||||
verifyTransaction(preparedPayoutTx);
|
verifyTransaction(preparedPayoutTx);
|
||||||
|
|
||||||
|
@ -772,7 +778,9 @@ public class TradeWalletService {
|
||||||
// take care of sorting!
|
// take care of sorting!
|
||||||
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
|
||||||
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
|
||||||
ECKey.ECDSASignature tradersSignature = tradersAddressEntry.getKeyPair().sign(sigHash, aesKey).toCanonicalised();
|
DeterministicKey keyPair = tradersAddressEntry.getKeyPair();
|
||||||
|
checkNotNull(keyPair);
|
||||||
|
ECKey.ECDSASignature tradersSignature = keyPair.sign(sigHash, aesKey).toCanonicalised();
|
||||||
|
|
||||||
TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false);
|
TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false);
|
||||||
TransactionSignature arbitratorTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(arbitratorSignature),
|
TransactionSignature arbitratorTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(arbitratorSignature),
|
||||||
|
@ -805,6 +813,7 @@ public class TradeWalletService {
|
||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
public void broadcastTx(Transaction tx, FutureCallback<Transaction> callback) {
|
public void broadcastTx(Transaction tx, FutureCallback<Transaction> callback) {
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
ListenableFuture<Transaction> future = walletAppKit.peerGroup().broadcastTransaction(tx).future();
|
ListenableFuture<Transaction> future = walletAppKit.peerGroup().broadcastTransaction(tx).future();
|
||||||
Futures.addCallback(future, callback);
|
Futures.addCallback(future, callback);
|
||||||
}
|
}
|
||||||
|
@ -850,6 +859,7 @@ public class TradeWalletService {
|
||||||
* @throws VerificationException
|
* @throws VerificationException
|
||||||
*/
|
*/
|
||||||
public Transaction getWalletTx(Sha256Hash txId) throws VerificationException {
|
public Transaction getWalletTx(Sha256Hash txId) throws VerificationException {
|
||||||
|
checkNotNull(wallet);
|
||||||
return wallet.getTransaction(txId);
|
return wallet.getTransaction(txId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,22 +868,27 @@ public class TradeWalletService {
|
||||||
* is old and doesn't have that data.
|
* is old and doesn't have that data.
|
||||||
*/
|
*/
|
||||||
public int getLastBlockSeenHeight() {
|
public int getLastBlockSeenHeight() {
|
||||||
|
checkNotNull(wallet);
|
||||||
return wallet.getLastBlockSeenHeight();
|
return wallet.getLastBlockSeenHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<StoredBlock> getBlockHeightFuture(Transaction transaction) {
|
public ListenableFuture<StoredBlock> getBlockHeightFuture(Transaction transaction) {
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
return walletAppKit.chain().getHeightFuture((int) transaction.getLockTime());
|
return walletAppKit.chain().getHeightFuture((int) transaction.getLockTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBestChainHeight() {
|
public int getBestChainHeight() {
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
return walletAppKit.chain().getBestChainHeight();
|
return walletAppKit.chain().getBestChainHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBlockChainListener(BlockChainListener blockChainListener) {
|
public void addBlockChainListener(BlockChainListener blockChainListener) {
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
walletAppKit.chain().addListener(blockChainListener);
|
walletAppKit.chain().addListener(blockChainListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeBlockChainListener(BlockChainListener blockChainListener) {
|
public void removeBlockChainListener(BlockChainListener blockChainListener) {
|
||||||
|
checkNotNull(walletAppKit);
|
||||||
walletAppKit.chain().removeListener(blockChainListener);
|
walletAppKit.chain().removeListener(blockChainListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,7 +958,7 @@ public class TradeWalletService {
|
||||||
transaction.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString));
|
transaction.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString));
|
||||||
transaction.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString));
|
transaction.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString));
|
||||||
if (lockTime != 0) {
|
if (lockTime != 0) {
|
||||||
log.info("We use a locktime of " + lockTime);
|
log.info("We use a lockTime of " + lockTime);
|
||||||
// When using lockTime we need to set sequenceNumber to 0
|
// When using lockTime we need to set sequenceNumber to 0
|
||||||
transaction.getInputs().stream().forEach(i -> i.setSequenceNumber(0));
|
transaction.getInputs().stream().forEach(i -> i.setSequenceNumber(0));
|
||||||
transaction.setLockTime(lockTime);
|
transaction.setLockTime(lockTime);
|
||||||
|
@ -964,6 +979,7 @@ public class TradeWalletService {
|
||||||
private void signInput(Transaction transaction, TransactionInput input, int inputIndex) throws SigningException {
|
private void signInput(Transaction transaction, TransactionInput input, int inputIndex) throws SigningException {
|
||||||
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();
|
||||||
|
checkNotNull(wallet);
|
||||||
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
|
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
|
||||||
checkNotNull(sigKey, "signInput: sigKey must not be null. input.getOutpoint()=" + input.getOutpoint().toString());
|
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);
|
||||||
|
@ -981,6 +997,7 @@ public class TradeWalletService {
|
||||||
private void checkWalletConsistency() throws WalletException {
|
private void checkWalletConsistency() throws WalletException {
|
||||||
try {
|
try {
|
||||||
log.trace("Check if wallet is consistent before commit.");
|
log.trace("Check if wallet is consistent before commit.");
|
||||||
|
checkNotNull(wallet);
|
||||||
checkState(wallet.isConsistent());
|
checkState(wallet.isConsistent());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
|
|
|
@ -120,9 +120,9 @@ public class WalletService {
|
||||||
|
|
||||||
Threading.USER_THREAD = UserThread.getExecutor();
|
Threading.USER_THREAD = UserThread.getExecutor();
|
||||||
|
|
||||||
Timer timeoutTimer = UserThread.runAfter(() -> {
|
Timer timeoutTimer = UserThread.runAfter(() ->
|
||||||
exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT_SEC + " seconds."));
|
exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " +
|
||||||
}, STARTUP_TIMEOUT_SEC);
|
STARTUP_TIMEOUT_SEC + " seconds.")), STARTUP_TIMEOUT_SEC);
|
||||||
|
|
||||||
// If seed is non-null it means we are restoring from backup.
|
// If seed is non-null it means we are restoring from backup.
|
||||||
walletAppKit = new WalletAppKit(params, walletDir, "Bitsquare") {
|
walletAppKit = new WalletAppKit(params, walletDir, "Bitsquare") {
|
||||||
|
@ -190,7 +190,7 @@ public class WalletService {
|
||||||
timeoutTimer.cancel();
|
timeoutTimer.cancel();
|
||||||
|
|
||||||
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
|
// onSetupCompleted in walletAppKit is not the called on the last invocations, so we add a bit of delay
|
||||||
UserThread.runAfter(() -> resultHandler.handleResult(), 100, TimeUnit.MILLISECONDS);
|
UserThread.runAfter(resultHandler::handleResult, 100, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,8 +451,7 @@ public class WalletService {
|
||||||
public Coin getRequiredFee(String fromAddress,
|
public Coin getRequiredFee(String fromAddress,
|
||||||
String toAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
@Nullable KeyParameter aesKey) throws AddressFormatException, IllegalArgumentException,
|
@Nullable KeyParameter aesKey) throws AddressFormatException, IllegalArgumentException {
|
||||||
InsufficientMoneyException {
|
|
||||||
Coin fee;
|
Coin fee;
|
||||||
try {
|
try {
|
||||||
wallet.completeTx(getSendRequest(fromAddress, toAddress, amount, aesKey));
|
wallet.completeTx(getSendRequest(fromAddress, toAddress, amount, aesKey));
|
||||||
|
@ -469,7 +468,7 @@ public class WalletService {
|
||||||
String toAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
@Nullable KeyParameter aesKey) throws AddressFormatException,
|
@Nullable KeyParameter aesKey) throws AddressFormatException,
|
||||||
IllegalArgumentException, InsufficientMoneyException {
|
IllegalArgumentException {
|
||||||
Coin fee;
|
Coin fee;
|
||||||
try {
|
try {
|
||||||
wallet.completeTx(getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, null, aesKey));
|
wallet.completeTx(getSendRequestForMultipleAddresses(fromAddresses, toAddress, amount, null, aesKey));
|
||||||
|
@ -482,7 +481,7 @@ public class WalletService {
|
||||||
return fee;
|
return fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet.SendRequest getSendRequest(String fromAddress,
|
private Wallet.SendRequest getSendRequest(String fromAddress,
|
||||||
String toAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
@Nullable KeyParameter aesKey) throws AddressFormatException,
|
@Nullable KeyParameter aesKey) throws AddressFormatException,
|
||||||
|
@ -505,7 +504,7 @@ public class WalletService {
|
||||||
return sendRequest;
|
return sendRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Wallet.SendRequest getSendRequestForMultipleAddresses(Set<String> fromAddresses,
|
private Wallet.SendRequest getSendRequestForMultipleAddresses(Set<String> fromAddresses,
|
||||||
String toAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
@Nullable String changeAddress,
|
@Nullable String changeAddress,
|
||||||
|
@ -520,9 +519,9 @@ public class WalletService {
|
||||||
sendRequest.aesKey = aesKey;
|
sendRequest.aesKey = aesKey;
|
||||||
sendRequest.shuffleOutputs = false;
|
sendRequest.shuffleOutputs = false;
|
||||||
Set<AddressEntry> addressEntries = fromAddresses.stream()
|
Set<AddressEntry> addressEntries = fromAddresses.stream()
|
||||||
.map(e -> getAddressEntryByAddress(e))
|
.map(this::getAddressEntryByAddress)
|
||||||
.filter(e -> e.isPresent())
|
.filter(Optional::isPresent)
|
||||||
.map(e -> e.get()).collect(Collectors.toSet());
|
.map(Optional::get).collect(Collectors.toSet());
|
||||||
if (addressEntries.isEmpty())
|
if (addressEntries.isEmpty())
|
||||||
throw new IllegalArgumentException("No withdrawFromAddresses not found in our wallets.\n\t" +
|
throw new IllegalArgumentException("No withdrawFromAddresses not found in our wallets.\n\t" +
|
||||||
"fromAddresses=" + fromAddresses);
|
"fromAddresses=" + fromAddresses);
|
||||||
|
|
|
@ -44,8 +44,8 @@ public class MarketPriceFeed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long PERIOD_FIAT = 1; // We load only the selected currency on interval. Only the first request we load all
|
private static final long PERIOD_FIAT = 1; // We load only the selected currency on interval. Only the first request we load all
|
||||||
private static long PERIOD_CRYPTO = 10; // We load the full list with 33kb so we don't want to load too often
|
private static final long PERIOD_CRYPTO = 10; // We load the full list with 33kb so we don't want to load too often
|
||||||
|
|
||||||
private final ScheduledThreadPoolExecutor executorService = Utilities.getScheduledThreadPoolExecutor("MarketPriceFeed", 5, 10, 700L);
|
private final ScheduledThreadPoolExecutor executorService = Utilities.getScheduledThreadPoolExecutor("MarketPriceFeed", 5, 10, 700L);
|
||||||
private final Map<String, MarketPrice> cache = new HashMap<>();
|
private final Map<String, MarketPrice> cache = new HashMap<>();
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class Country implements Serializable {
|
||||||
|
|
||||||
public final String code;
|
public final String code;
|
||||||
public final String name;
|
public final String name;
|
||||||
public final Region region;
|
private final Region region;
|
||||||
|
|
||||||
public Country(String code, String name, Region region) {
|
public Country(String code, String name, Region region) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
|
|
|
@ -158,6 +158,7 @@ public class CurrencyUtil {
|
||||||
return !(isCryptoCurrency(currencyCode)) && Currency.getInstance(currencyCode) != null;
|
return !(isCryptoCurrency(currencyCode)) && Currency.getInstance(currencyCode) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public static boolean isCryptoCurrency(String currencyCode) {
|
public static boolean isCryptoCurrency(String currencyCode) {
|
||||||
return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny().isPresent();
|
return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findAny().isPresent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class FiatCurrency extends TradeCurrency implements Serializable {
|
||||||
this(Currency.getInstance(currencyCode));
|
this(Currency.getInstance(currencyCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public FiatCurrency(Currency currency) {
|
public FiatCurrency(Currency currency) {
|
||||||
super(currency.getCurrencyCode(), currency.getDisplayName(Preferences.getDefaultLocale()), currency.getSymbol());
|
super(currency.getCurrencyCode(), currency.getDisplayName(Preferences.getDefaultLocale()), currency.getSymbol());
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
|
|
|
@ -40,11 +40,11 @@ public class PaymentAccount implements Serializable {
|
||||||
protected final Date creationDate;
|
protected final Date creationDate;
|
||||||
protected final PaymentMethod paymentMethod;
|
protected final PaymentMethod paymentMethod;
|
||||||
protected String accountName;
|
protected String accountName;
|
||||||
protected final List<TradeCurrency> tradeCurrencies = new ArrayList<>();
|
final List<TradeCurrency> tradeCurrencies = new ArrayList<>();
|
||||||
protected TradeCurrency selectedTradeCurrency;
|
protected TradeCurrency selectedTradeCurrency;
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Country country = null;
|
protected Country country = null;
|
||||||
protected PaymentAccountContractData contractData;
|
PaymentAccountContractData contractData;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -52,7 +52,7 @@ public class PaymentAccount implements Serializable {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
public PaymentAccount(PaymentMethod paymentMethod) {
|
protected PaymentAccount(PaymentMethod paymentMethod) {
|
||||||
this.paymentMethod = paymentMethod;
|
this.paymentMethod = paymentMethod;
|
||||||
id = UUID.randomUUID().toString();
|
id = UUID.randomUUID().toString();
|
||||||
creationDate = new Date();
|
creationDate = new Date();
|
||||||
|
@ -108,7 +108,7 @@ public class PaymentAccount implements Serializable {
|
||||||
return country;
|
return country;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCountry(@Nullable Country country) {
|
public void setCountry(Country country) {
|
||||||
this.country = country;
|
this.country = country;
|
||||||
contractData.setCountryCode(country.code);
|
contractData.setCountryCode(country.code);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ public abstract class PaymentAccountContractData implements Serializable {
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public PaymentAccountContractData(String paymentMethodName, String id, int maxTradePeriod) {
|
PaymentAccountContractData(String paymentMethodName, String id, int maxTradePeriod) {
|
||||||
this.paymentMethodName = paymentMethodName;
|
this.paymentMethodName = paymentMethodName;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.maxTradePeriod = maxTradePeriod;
|
this.maxTradePeriod = maxTradePeriod;
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class SepaAccountContractData extends PaymentAccountContractData implemen
|
||||||
super(paymentMethod, id, maxTradePeriod);
|
super(paymentMethod, id, maxTradePeriod);
|
||||||
Set<String> acceptedCountryCodesAsSet = CountryUtil.getAllSepaCountries().stream().map(e -> e.code).collect(Collectors.toSet());
|
Set<String> acceptedCountryCodesAsSet = CountryUtil.getAllSepaCountries().stream().map(e -> e.code).collect(Collectors.toSet());
|
||||||
acceptedCountryCodes = new ArrayList<>(acceptedCountryCodesAsSet);
|
acceptedCountryCodes = new ArrayList<>(acceptedCountryCodesAsSet);
|
||||||
acceptedCountryCodes.sort((a, b) -> a.compareTo(b));
|
acceptedCountryCodes.sort(String::compareTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHolderName(String holderName) {
|
public void setHolderName(String holderName) {
|
||||||
|
|
|
@ -36,11 +36,11 @@ public abstract class SellerTrade extends Trade implements Serializable {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class);
|
private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class);
|
||||||
|
|
||||||
public SellerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage<? extends TradableList> storage) {
|
SellerTrade(Offer offer, Coin tradeAmount, NodeAddress tradingPeerNodeAddress, Storage<? extends TradableList> storage) {
|
||||||
super(offer, tradeAmount, tradingPeerNodeAddress, storage);
|
super(offer, tradeAmount, tradingPeerNodeAddress, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SellerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
SellerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
||||||
super(offer, storage);
|
super(offer, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -554,6 +554,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
||||||
this.takeOfferFeeTxId = takeOfferFeeTxId;
|
this.takeOfferFeeTxId = takeOfferFeeTxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@org.jetbrains.annotations.Nullable
|
||||||
public String getTakeOfferFeeTxId() {
|
public String getTakeOfferFeeTxId() {
|
||||||
return takeOfferFeeTxId;
|
return takeOfferFeeTxId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ import io.bitsquare.common.handlers.ResultHandler;
|
||||||
import io.bitsquare.common.util.JsonExclude;
|
import io.bitsquare.common.util.JsonExclude;
|
||||||
import io.bitsquare.locale.Country;
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
import io.bitsquare.p2p.storage.data.RequiresLiveOwner;
|
import io.bitsquare.p2p.storage.messages.RequiresLiveOwnerData;
|
||||||
import io.bitsquare.p2p.storage.data.StorageMessage;
|
import io.bitsquare.p2p.storage.messages.StorageMessage;
|
||||||
import io.bitsquare.payment.PaymentMethod;
|
import io.bitsquare.payment.PaymentMethod;
|
||||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
|
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
|
||||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
|
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
|
||||||
|
@ -47,16 +47,14 @@ import java.util.concurrent.TimeUnit;
|
||||||
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;
|
||||||
|
|
||||||
public final class Offer implements StorageMessage, RequiresLiveOwner {
|
public final class Offer implements StorageMessage, RequiresLiveOwnerData {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
|
||||||
@JsonExclude
|
@JsonExclude
|
||||||
private static final Logger log = LoggerFactory.getLogger(Offer.class);
|
private static final Logger log = LoggerFactory.getLogger(Offer.class);
|
||||||
|
|
||||||
//public static final long TTL = TimeUnit.MINUTES.toMillis(10);
|
public static final long TTL = TimeUnit.SECONDS.toMillis(60);
|
||||||
//TODO
|
|
||||||
public static final long TTL = TimeUnit.SECONDS.toMillis(10);
|
|
||||||
|
|
||||||
public final static String TAC_OFFERER = "When placing that offer I accept that anyone who fulfills my conditions can " +
|
public final static String TAC_OFFERER = "When placing that offer I accept that anyone who fulfills my conditions can " +
|
||||||
"take that offer.";
|
"take that offer.";
|
||||||
|
|
|
@ -21,7 +21,7 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
import io.bitsquare.p2p.P2PService;
|
import io.bitsquare.p2p.P2PService;
|
||||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ public class OfferBookService {
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
private final List<OfferBookChangedListener> offerBookChangedListeners = new LinkedList<>();
|
private final List<OfferBookChangedListener> offerBookChangedListeners = new LinkedList<>();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -57,20 +58,18 @@ public class OfferBookService {
|
||||||
|
|
||||||
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
|
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAdded(ProtectedData entry) {
|
public void onAdded(ProtectedData data) {
|
||||||
log.debug("OfferBookService.onAdded " + entry);
|
|
||||||
offerBookChangedListeners.stream().forEach(listener -> {
|
offerBookChangedListeners.stream().forEach(listener -> {
|
||||||
if (entry.expirableMessage instanceof Offer)
|
if (data.expirableMessage instanceof Offer)
|
||||||
listener.onAdded((Offer) entry.expirableMessage);
|
listener.onAdded((Offer) data.expirableMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoved(ProtectedData entry) {
|
public void onRemoved(ProtectedData data) {
|
||||||
offerBookChangedListeners.stream().forEach(listener -> {
|
offerBookChangedListeners.stream().forEach(listener -> {
|
||||||
log.debug("OfferBookService.onRemoved " + entry);
|
if (data.expirableMessage instanceof Offer)
|
||||||
if (entry.expirableMessage instanceof Offer)
|
listener.onRemoved((Offer) data.expirableMessage);
|
||||||
listener.onRemoved((Offer) entry.expirableMessage);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -101,7 +100,7 @@ public class OfferBookService {
|
||||||
result = p2PService.addData(offer);
|
result = p2PService.addData(offer);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
log.trace("Add offer to network was successful. Offer = " + offer);
|
log.trace("Add offer to network was successful. Offer ID = " + offer.getId());
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
} else {
|
} else {
|
||||||
errorMessageHandler.handleErrorMessage("Add offer failed");
|
errorMessageHandler.handleErrorMessage("Add offer failed");
|
||||||
|
@ -110,7 +109,7 @@ public class OfferBookService {
|
||||||
|
|
||||||
public void removeOffer(Offer offer, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
public void removeOffer(Offer offer, @Nullable ResultHandler resultHandler, @Nullable ErrorMessageHandler errorMessageHandler) {
|
||||||
if (p2PService.removeData(offer)) {
|
if (p2PService.removeData(offer)) {
|
||||||
log.trace("Remove offer from network was successful. Offer = " + offer);
|
log.trace("Remove offer from network was successful. Offer ID = " + offer.getId());
|
||||||
if (resultHandler != null)
|
if (resultHandler != null)
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
} else {
|
} else {
|
||||||
|
@ -121,8 +120,8 @@ public class OfferBookService {
|
||||||
|
|
||||||
public List<Offer> getOffers() {
|
public List<Offer> getOffers() {
|
||||||
return p2PService.getDataMap().values().stream()
|
return p2PService.getDataMap().values().stream()
|
||||||
.filter(e -> e.expirableMessage instanceof Offer)
|
.filter(data -> data.expirableMessage instanceof Offer)
|
||||||
.map(e -> (Offer) e.expirableMessage)
|
.map(data -> (Offer) data.expirableMessage)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,8 @@ public class OpenOfferManager {
|
||||||
if (bootstrapListener != null)
|
if (bootstrapListener != null)
|
||||||
p2PService.removeP2PServiceListener(bootstrapListener);
|
p2PService.removeP2PServiceListener(bootstrapListener);
|
||||||
|
|
||||||
long period = (long) (Offer.TTL * 0.8); // republish sufficiently before offer would expire
|
// republish sufficiently before offer would expire
|
||||||
|
long period = (long) (Offer.TTL * 0.7);
|
||||||
TimerTask timerTask = new TimerTask() {
|
TimerTask timerTask = new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -178,6 +179,7 @@ public class OpenOfferManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
shutDown(null);
|
shutDown(null);
|
||||||
}
|
}
|
||||||
|
@ -190,8 +192,7 @@ public class OpenOfferManager {
|
||||||
log.info("remove all open offers at shutDown");
|
log.info("remove all open offers at shutDown");
|
||||||
shutDownRequested = true;
|
shutDownRequested = true;
|
||||||
// we remove own offers from offerbook when we go offline
|
// we remove own offers from offerbook when we go offline
|
||||||
//TODO
|
openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
||||||
// openOffers.forEach(openOffer -> offerBookService.removeOfferAtShutDown(openOffer.getOffer()));
|
|
||||||
|
|
||||||
if (completeHandler != null)
|
if (completeHandler != null)
|
||||||
UserThread.runAfter(completeHandler::run, openOffers.size() * 200 + 300, TimeUnit.MILLISECONDS);
|
UserThread.runAfter(completeHandler::run, openOffers.size() * 200 + 300, TimeUnit.MILLISECONDS);
|
||||||
|
@ -230,7 +231,9 @@ public class OpenOfferManager {
|
||||||
log.warn("Offer was not found in our list of open offers. We still try to remove it from the offerbook.");
|
log.warn("Offer was not found in our list of open offers. We still try to remove it from the offerbook.");
|
||||||
errorMessageHandler.handleErrorMessage("Offer was not found in our list of open offers. " +
|
errorMessageHandler.handleErrorMessage("Offer was not found in our list of open offers. " +
|
||||||
"We still try to remove it from the offerbook.");
|
"We still try to remove it from the offerbook.");
|
||||||
onRemoveOffer(offer);
|
offerBookService.removeOffer(offer,
|
||||||
|
() -> offer.setState(Offer.State.REMOVED),
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,14 +250,6 @@ public class OpenOfferManager {
|
||||||
errorMessageHandler);
|
errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// That should not be needed, but there are cases where the openOffer is removed but the offer still in the
|
|
||||||
// offerbook
|
|
||||||
public void onRemoveOffer(Offer offer) {
|
|
||||||
offerBookService.removeOffer(offer,
|
|
||||||
() -> offer.setState(Offer.State.REMOVED),
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reserveOpenOffer(OpenOffer openOffer) {
|
public void reserveOpenOffer(OpenOffer openOffer) {
|
||||||
openOffer.setState(OpenOffer.State.RESERVED);
|
openOffer.setState(OpenOffer.State.RESERVED);
|
||||||
}
|
}
|
||||||
|
@ -283,7 +278,7 @@ public class OpenOfferManager {
|
||||||
openOffer.setState(OpenOffer.State.CLOSED);
|
openOffer.setState(OpenOffer.State.CLOSED);
|
||||||
offerBookService.removeOffer(openOffer.getOffer(),
|
offerBookService.removeOffer(openOffer.getOffer(),
|
||||||
() -> log.trace("Successful removed offer"),
|
() -> log.trace("Successful removed offer"),
|
||||||
errorMessage -> log.error(errorMessage));
|
log::error);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,4 @@ public class OfferAvailabilityModel implements Model {
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PubKeyRing getPubKeyRing() {
|
|
||||||
return pubKeyRing;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public abstract class OfferMessage implements DirectMessage {
|
||||||
private final int messageVersion = Version.getP2PMessageVersion();
|
private final int messageVersion = Version.getP2PMessageVersion();
|
||||||
public final String offerId;
|
public final String offerId;
|
||||||
|
|
||||||
protected OfferMessage(String offerId) {
|
OfferMessage(String offerId) {
|
||||||
this.offerId = offerId;
|
this.offerId = offerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
||||||
|
|
||||||
model.p2PService.sendEncryptedDirectMessage(model.getPeerNodeAddress(),
|
model.p2PService.sendEncryptedDirectMessage(model.getPeerNodeAddress(),
|
||||||
model.offer.getPubKeyRing(),
|
model.offer.getPubKeyRing(),
|
||||||
new OfferAvailabilityRequest(model.offer.getId(), model.getPubKeyRing()),
|
new OfferAvailabilityRequest(model.offer.getId(), model.pubKeyRing),
|
||||||
new SendDirectMessageListener() {
|
new SendDirectMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onArrived() {
|
public void onArrived() {
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class PlaceOfferProtocol {
|
||||||
model.offerAddedToOfferBook = false;
|
model.offerAddedToOfferBook = false;
|
||||||
log.debug("Offer removed from offer book.");
|
log.debug("Offer removed from offer book.");
|
||||||
},
|
},
|
||||||
errorMessage2 -> log.error(errorMessage2));
|
log::error);
|
||||||
}
|
}
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,8 @@ public class SignPayoutTx extends TradeTask {
|
||||||
|
|
||||||
// We use the sellers LastBlockSeenHeight, which might be different to the buyers one.
|
// We use the sellers LastBlockSeenHeight, which might be different to the buyers one.
|
||||||
// If lock time is 0 we set lockTimeAsBlockHeight to 0 to mark it as "not set".
|
// If lock time is 0 we set lockTimeAsBlockHeight to 0 to mark it as "not set".
|
||||||
// In the tradewallet we apply the locktime only if it is set, otherwise we use the default values for
|
// In the tradeWallet we apply the lockTime only if it is set, otherwise we use the default values for
|
||||||
// transaction locktime and sequence number
|
// transaction lockTime and sequence number
|
||||||
long lockTime = trade.getOffer().getPaymentMethod().getLockTime();
|
long lockTime = trade.getOffer().getPaymentMethod().getLockTime();
|
||||||
long lockTimeAsBlockHeight = 0;
|
long lockTimeAsBlockHeight = 0;
|
||||||
if (lockTime > 0)
|
if (lockTime > 0)
|
||||||
|
|
|
@ -386,7 +386,7 @@ public class Preferences implements Serializable {
|
||||||
|
|
||||||
public boolean showAgain(String key) {
|
public boolean showAgain(String key) {
|
||||||
// if we add new and those are not in our stored map we display by default the new popup
|
// if we add new and those are not in our stored map we display by default the new popup
|
||||||
if (!getShowAgainMap().containsKey(key)) {
|
if (!showAgainMap.containsKey(key)) {
|
||||||
showAgainMap.put(key, true);
|
showAgainMap.put(key, true);
|
||||||
storage.queueUpForSave(2000);
|
storage.queueUpForSave(2000);
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ public class User implements Serializable {
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegisteredArbitrator(Arbitrator arbitrator) {
|
public void setRegisteredArbitrator(@org.jetbrains.annotations.Nullable Arbitrator arbitrator) {
|
||||||
this.registeredArbitrator = arbitrator;
|
this.registeredArbitrator = arbitrator;
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||||
.show();
|
.show();
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (AddressFormatException | InsufficientMoneyException e) {
|
} catch (AddressFormatException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.net.URL;
|
||||||
// TODO route over tor
|
// TODO route over tor
|
||||||
public class HttpClient implements Serializable {
|
public class HttpClient implements Serializable {
|
||||||
|
|
||||||
private String baseUrl;
|
private final String baseUrl;
|
||||||
|
|
||||||
public HttpClient(String baseUrl) {
|
public HttpClient(String baseUrl) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
|
|
|
@ -24,11 +24,11 @@ import io.bitsquare.p2p.peers.peerexchange.PeerExchangeManager;
|
||||||
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;
|
||||||
import io.bitsquare.p2p.storage.data.ExpirableMessage;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
import io.bitsquare.p2p.storage.data.MailboxMessage;
|
import io.bitsquare.p2p.storage.ProtectedMailboxData;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
|
||||||
import io.bitsquare.p2p.storage.messages.AddDataMessage;
|
import io.bitsquare.p2p.storage.messages.AddDataMessage;
|
||||||
|
import io.bitsquare.p2p.storage.messages.ExpirableMessage;
|
||||||
|
import io.bitsquare.p2p.storage.messages.MailboxMessage;
|
||||||
import javafx.beans.property.*;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
@ -110,9 +110,9 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(boolean useLocalhost, int networkId, File storageDir) {
|
private void init(boolean useLocalhost, int networkId, File storageDir) {
|
||||||
connectionNodeAddressListener = (observable, oldValue, newValue) -> {
|
connectionNodeAddressListener = (observable, oldValue, newValue) ->
|
||||||
UserThread.execute(() -> numConnectedPeers.set(networkNode.getNodeAddressesOfConfirmedConnections().size()));
|
UserThread.execute(() ->
|
||||||
};
|
numConnectedPeers.set(networkNode.getNodeAddressesOfConfirmedConnections().size()));
|
||||||
|
|
||||||
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
|
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
|
||||||
networkNode.addConnectionListener(this);
|
networkNode.addConnectionListener(this);
|
||||||
|
@ -364,13 +364,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAdded(ProtectedData entry) {
|
public void onAdded(ProtectedData data) {
|
||||||
if (entry instanceof ProtectedMailboxData)
|
if (data instanceof ProtectedMailboxData)
|
||||||
processProtectedMailboxData((ProtectedMailboxData) entry);
|
processProtectedMailboxData((ProtectedMailboxData) data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoved(ProtectedData entry) {
|
public void onRemoved(ProtectedData data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,9 +59,7 @@ public class Connection implements MessageListener {
|
||||||
private static final int MAX_MSG_SIZE = 100 * 1024; // 100 kb of compressed data
|
private static final int MAX_MSG_SIZE = 100 * 1024; // 100 kb of compressed data
|
||||||
private static final int MSG_THROTTLE_PER_SEC = 10; // With MAX_MSG_SIZE of 100kb results in bandwidth of 10 mbit/sec
|
private static final int MSG_THROTTLE_PER_SEC = 10; // With MAX_MSG_SIZE of 100kb results in bandwidth of 10 mbit/sec
|
||||||
private static final int MSG_THROTTLE_PER_10SEC = 50; // With MAX_MSG_SIZE of 100kb results in bandwidth of 5 mbit/sec for 10 sec
|
private static final int MSG_THROTTLE_PER_10SEC = 50; // With MAX_MSG_SIZE of 100kb results in bandwidth of 5 mbit/sec for 10 sec
|
||||||
//private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(60);
|
private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(60);
|
||||||
//TODO
|
|
||||||
private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(6);
|
|
||||||
|
|
||||||
public static int getMaxMsgSize() {
|
public static int getMaxMsgSize() {
|
||||||
return MAX_MSG_SIZE;
|
return MAX_MSG_SIZE;
|
||||||
|
@ -695,6 +693,8 @@ public class Connection implements MessageListener {
|
||||||
messageListener.onMessage(message, connection);
|
messageListener.onMessage(message, connection);
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException | NoClassDefFoundError e) {
|
} catch (ClassNotFoundException | NoClassDefFoundError e) {
|
||||||
|
log.warn(e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
reportInvalidRequest(RuleViolation.INVALID_DATA_TYPE);
|
reportInvalidRequest(RuleViolation.INVALID_DATA_TYPE);
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Utilities;
|
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 org.apache.commons.lang3.StringUtils;
|
||||||
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;
|
||||||
|
@ -56,7 +57,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
abstract public void start(@Nullable SetupListener setupListener);
|
abstract public void start(@Nullable SetupListener setupListener);
|
||||||
|
|
||||||
public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peersNodeAddress, Message message) {
|
public SettableFuture<Connection> sendMessage(@NotNull NodeAddress peersNodeAddress, Message message) {
|
||||||
Log.traceCall("peersNodeAddress=" + peersNodeAddress + "\n\tmessage=" + message);
|
Log.traceCall("peersNodeAddress=" + peersNodeAddress + "\n\tmessage=" + StringUtils.abbreviate(message.toString(), 100));
|
||||||
checkNotNull(peersNodeAddress, "peerAddress must not be null");
|
checkNotNull(peersNodeAddress, "peerAddress must not be null");
|
||||||
|
|
||||||
Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peersNodeAddress);
|
Optional<Connection> outboundConnectionOptional = lookupOutboundConnection(peersNodeAddress);
|
||||||
|
@ -127,7 +128,7 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettableFuture<Connection> sendMessage(Connection connection, Message message) {
|
public SettableFuture<Connection> sendMessage(Connection connection, Message message) {
|
||||||
Log.traceCall("\n\tmessage=" + message + "\n\tconnection=" + connection);
|
Log.traceCall("\n\tmessage=" + StringUtils.abbreviate(message.toString(), 100) + "\n\tconnection=" + connection);
|
||||||
// connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block
|
// connection.sendMessage might take a bit (compression, write to stream), so we use a thread to not block
|
||||||
ListenableFuture<Connection> future = executorService.submit(() -> {
|
ListenableFuture<Connection> future = executorService.submit(() -> {
|
||||||
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + connection.getUid());
|
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + connection.getUid());
|
||||||
|
|
|
@ -31,20 +31,19 @@ public class Broadcaster {
|
||||||
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
|
||||||
private IntegerProperty numOfBroadcasts = new SimpleIntegerProperty(0);
|
private final 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 + "\n\t" +
|
Log.traceCall("Sender=" + sender + "\n\t" +
|
||||||
"Message=" + StringUtils.abbreviate(message.toString(), 100));
|
"Message=" + StringUtils.abbreviate(message.toString(), 100));
|
||||||
numOfBroadcasts.set(0);
|
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.", receivers.size());
|
||||||
receivers.stream()
|
receivers.stream()
|
||||||
.filter(connection -> !connection.getPeersNodeAddressOptional().get().equals(sender))
|
.filter(connection -> !connection.getPeersNodeAddressOptional().get().equals(sender))
|
||||||
.forEach(connection -> {
|
.forEach(connection -> {
|
||||||
|
@ -70,7 +69,7 @@ public class Broadcaster {
|
||||||
} else {
|
} else {
|
||||||
log.warn("Message not broadcasted because we have no available peers yet.\n\t" +
|
log.warn("Message not broadcasted because we have no available peers yet.\n\t" +
|
||||||
"That should never happen as broadcast should not be called in such cases.\n" +
|
"That should never happen as broadcast should not be called in such cases.\n" +
|
||||||
"message = {}", message);
|
"message = {}", StringUtils.abbreviate(message.toString(), 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,23 +29,23 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
|
|
||||||
private static int MAX_CONNECTIONS;
|
private static int MAX_CONNECTIONS;
|
||||||
private static int MIN_CONNECTIONS;
|
private static int MIN_CONNECTIONS;
|
||||||
private static int MAX_CONNECTIONS_EXTENDED_1;
|
private static int MAX_CONNECTIONS_PEER;
|
||||||
private static int MAX_CONNECTIONS_EXTENDED_2;
|
private static int MAX_CONNECTIONS_NON_DIRECT;
|
||||||
|
|
||||||
|
|
||||||
private static int MAX_CONNECTIONS_EXTENDED_3;
|
private static int MAX_CONNECTIONS_ABSOLUTE;
|
||||||
private boolean printReportedPeersDetails = true;
|
private final boolean printReportedPeersDetails = true;
|
||||||
|
|
||||||
public static void setMaxConnections(int maxConnections) {
|
public static void setMaxConnections(int maxConnections) {
|
||||||
MAX_CONNECTIONS = maxConnections;
|
MAX_CONNECTIONS = maxConnections;
|
||||||
MIN_CONNECTIONS = Math.max(1, maxConnections - 4);
|
MIN_CONNECTIONS = Math.max(1, maxConnections - 4);
|
||||||
MAX_CONNECTIONS_EXTENDED_1 = MAX_CONNECTIONS + 5;
|
MAX_CONNECTIONS_PEER = MAX_CONNECTIONS + 5;
|
||||||
MAX_CONNECTIONS_EXTENDED_2 = MAX_CONNECTIONS + 10;
|
MAX_CONNECTIONS_NON_DIRECT = MAX_CONNECTIONS + 10;
|
||||||
MAX_CONNECTIONS_EXTENDED_3 = MAX_CONNECTIONS + 20;
|
MAX_CONNECTIONS_ABSOLUTE = MAX_CONNECTIONS + 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
setMaxConnections(3);
|
setMaxConnections(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int MAX_REPORTED_PEERS = 1000;
|
private static final int MAX_REPORTED_PEERS = 1000;
|
||||||
|
@ -98,7 +98,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxConnections() {
|
public int getMaxConnections() {
|
||||||
return MAX_CONNECTIONS_EXTENDED_3;
|
return MAX_CONNECTIONS_ABSOLUTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -161,7 +161,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
log.info("We have {} connections open. Our limit is {}", size, limit);
|
log.info("We have {} connections open. Our limit is {}", size, limit);
|
||||||
if (size > limit) {
|
if (size > limit) {
|
||||||
// Only InboundConnection, and PEER type connections
|
// Only InboundConnection, and PEER type connections
|
||||||
log.info("We have too many connections open. We try to close some.\n\t" +
|
log.info("We have too many connections open.\n\t" +
|
||||||
"Lets try first to remove the inbound connections of type PEER.");
|
"Lets try first to remove the inbound connections of type PEER.");
|
||||||
List<Connection> candidates = allConnections.stream()
|
List<Connection> candidates = allConnections.stream()
|
||||||
.filter(e -> e instanceof InboundConnection)
|
.filter(e -> e instanceof InboundConnection)
|
||||||
|
@ -169,19 +169,19 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (candidates.size() == 0) {
|
if (candidates.size() == 0) {
|
||||||
log.info("No candidates found. We go to the next level and check if we exceed our " +
|
log.info("No candidates found. We check if we exceed our " +
|
||||||
"MAX_CONNECTIONS_EXTENDED_1 limit of {}", MAX_CONNECTIONS_EXTENDED_1);
|
"MAX_CONNECTIONS_PEER limit of {}", MAX_CONNECTIONS_PEER);
|
||||||
if (size > MAX_CONNECTIONS_EXTENDED_1) {
|
if (size > MAX_CONNECTIONS_PEER) {
|
||||||
log.info("Lets try to remove any connection of type PEER.");
|
log.info("Lets try to remove ANY connection of type PEER.");
|
||||||
// Only PEER type connections
|
// Only PEER type connections
|
||||||
candidates = allConnections.stream()
|
candidates = allConnections.stream()
|
||||||
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
.filter(e -> e.getPeerType() == Connection.PeerType.PEER)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (candidates.size() == 0) {
|
if (candidates.size() == 0) {
|
||||||
log.info("No candidates found. We go to the next level and check if we exceed our " +
|
log.info("No candidates found. We check if we exceed our " +
|
||||||
"MAX_CONNECTIONS_EXTENDED_2 limit of {}", MAX_CONNECTIONS_EXTENDED_2);
|
"MAX_CONNECTIONS_NON_DIRECT limit of {}", MAX_CONNECTIONS_NON_DIRECT);
|
||||||
if (size > MAX_CONNECTIONS_EXTENDED_2) {
|
if (size > MAX_CONNECTIONS_NON_DIRECT) {
|
||||||
log.info("Lets try to remove any connection which is not of type DIRECT_MSG_PEER.");
|
log.info("Lets try to remove any connection which is not of type DIRECT_MSG_PEER.");
|
||||||
// All connections except DIRECT_MSG_PEER
|
// All connections except DIRECT_MSG_PEER
|
||||||
candidates = allConnections.stream()
|
candidates = allConnections.stream()
|
||||||
|
@ -189,9 +189,9 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (candidates.size() == 0) {
|
if (candidates.size() == 0) {
|
||||||
log.info("No candidates found. We go to the next level and check if we exceed our " +
|
log.info("No candidates found. We check if we exceed our " +
|
||||||
"MAX_CONNECTIONS_EXTENDED_3 limit of {}", MAX_CONNECTIONS_EXTENDED_3);
|
"MAX_CONNECTIONS_ABSOLUTE limit of {}", MAX_CONNECTIONS_ABSOLUTE);
|
||||||
if (size > MAX_CONNECTIONS_EXTENDED_3) {
|
if (size > MAX_CONNECTIONS_ABSOLUTE) {
|
||||||
log.info("Lets try to remove any connection.");
|
log.info("Lets try to remove any connection.");
|
||||||
// All connections
|
// All connections
|
||||||
candidates = allConnections.stream()
|
candidates = allConnections.stream()
|
||||||
|
@ -225,7 +225,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
private void removeSuperfluousSeedNodes() {
|
private void removeSuperfluousSeedNodes() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
Set<Connection> allConnections = networkNode.getAllConnections();
|
Set<Connection> allConnections = networkNode.getAllConnections();
|
||||||
if (allConnections.size() > MAX_CONNECTIONS_EXTENDED_1) {
|
if (allConnections.size() > MAX_CONNECTIONS_PEER) {
|
||||||
List<Connection> candidates = allConnections.stream()
|
List<Connection> candidates = allConnections.stream()
|
||||||
.filter(this::isSeedNode)
|
.filter(this::isSeedNode)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -266,8 +266,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
private void removeTooOldReportedPeers() {
|
private void removeTooOldReportedPeers() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
Set<ReportedPeer> reportedPeersToRemove = reportedPeers.stream()
|
Set<ReportedPeer> reportedPeersToRemove = reportedPeers.stream()
|
||||||
.filter(reportedPeer -> reportedPeer.date != null &&
|
.filter(reportedPeer -> new Date().getTime() - reportedPeer.date.getTime() > MAX_AGE)
|
||||||
new Date().getTime() - reportedPeer.date.getTime() > MAX_AGE)
|
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
reportedPeersToRemove.forEach(this::removeReportedPeer);
|
reportedPeersToRemove.forEach(this::removeReportedPeer);
|
||||||
}
|
}
|
||||||
|
@ -280,7 +279,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
printReportedPeers(reportedPeersToAdd);
|
printReportedPeers(reportedPeersToAdd);
|
||||||
|
|
||||||
// We check if the reported msg is not violating our rules
|
// We check if the reported msg is not violating our rules
|
||||||
if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + PeerManager.MAX_CONNECTIONS_EXTENDED_3 + 10)) {
|
if (reportedPeersToAdd.size() <= (MAX_REPORTED_PEERS + PeerManager.MAX_CONNECTIONS_ABSOLUTE + 10)) {
|
||||||
reportedPeers.addAll(reportedPeersToAdd);
|
reportedPeers.addAll(reportedPeersToAdd);
|
||||||
purgeReportedPeersIfExceeds();
|
purgeReportedPeersIfExceeds();
|
||||||
|
|
||||||
|
@ -311,7 +310,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printReportedPeers(HashSet<ReportedPeer> reportedPeers) {
|
private void printReportedPeers(HashSet<ReportedPeer> reportedPeers) {
|
||||||
if (printReportedPeersDetails) {
|
if (printReportedPeersDetails) {
|
||||||
StringBuilder result = new StringBuilder("We received now reportedPeers:");
|
StringBuilder result = new StringBuilder("We received now reportedPeers:");
|
||||||
reportedPeers.stream().forEach(e -> result.append("\n\t").append(e));
|
reportedPeers.stream().forEach(e -> result.append("\n\t").append(e));
|
||||||
|
@ -351,8 +350,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
private void removeTooOldPersistedPeers() {
|
private void removeTooOldPersistedPeers() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
Set<ReportedPeer> persistedPeersToRemove = persistedPeers.stream()
|
Set<ReportedPeer> persistedPeersToRemove = persistedPeers.stream()
|
||||||
.filter(reportedPeer -> reportedPeer.date != null &&
|
.filter(reportedPeer -> new Date().getTime() - reportedPeer.date.getTime() > MAX_AGE)
|
||||||
new Date().getTime() - reportedPeer.date.getTime() > MAX_AGE)
|
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
persistedPeersToRemove.forEach(this::removePersistedPeer);
|
persistedPeersToRemove.forEach(this::removePersistedPeer);
|
||||||
}
|
}
|
||||||
|
@ -447,7 +445,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
private void purgeReportedPeersIfExceeds() {
|
private void purgeReportedPeersIfExceeds() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
int size = getReportedPeers().size();
|
int size = getReportedPeers().size();
|
||||||
int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_EXTENDED_3;
|
int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_ABSOLUTE;
|
||||||
if (size > limit) {
|
if (size > limit) {
|
||||||
log.trace("We have already {} reported peers which exceeds our limit of {}." +
|
log.trace("We have already {} reported peers which exceeds our limit of {}." +
|
||||||
"We remove random peers from the reported peers list.", size, limit);
|
"We remove random peers from the reported peers list.", size, limit);
|
||||||
|
@ -466,7 +464,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
private void purgePersistedPeersIfExceeds() {
|
private void purgePersistedPeersIfExceeds() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
int size = getPersistedPeers().size();
|
int size = getPersistedPeers().size();
|
||||||
int limit = MAX_REPORTED_PEERS - MAX_CONNECTIONS_EXTENDED_3;
|
int limit = MAX_PERSISTED_PEERS - MAX_CONNECTIONS_ABSOLUTE;
|
||||||
if (size > limit) {
|
if (size > limit) {
|
||||||
log.trace("We have already {} persisted peers which exceeds our limit of {}." +
|
log.trace("We have already {} persisted peers which exceeds our limit of {}." +
|
||||||
"We remove random peers from the persisted peers list.", size, limit);
|
"We remove random peers from the persisted peers list.", size, limit);
|
||||||
|
@ -478,7 +476,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
removePersistedPeer(toRemove);
|
removePersistedPeer(toRemove);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.trace("No need to purge persisted peers.\n\tWe don't have more then {} persisted peers yet.", MAX_REPORTED_PEERS);
|
log.trace("No need to purge persisted peers.\n\tWe don't have more then {} persisted peers yet.", MAX_PERSISTED_PEERS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +485,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
return list.remove(new Random().nextInt(list.size()));
|
return list.remove(new Random().nextInt(list.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ReportedPeer> getConnectedPeers() {
|
private Set<ReportedPeer> getConnectedPeers() {
|
||||||
// networkNode.getConfirmedConnections includes:
|
// networkNode.getConfirmedConnections includes:
|
||||||
// filter(connection -> connection.getPeersNodeAddressOptional().isPresent())
|
// filter(connection -> connection.getPeersNodeAddressOptional().isPresent())
|
||||||
return networkNode.getConfirmedConnections().stream()
|
return networkNode.getConfirmedConnections().stream()
|
||||||
|
@ -495,7 +493,7 @@ public class PeerManager implements ConnectionListener, MessageListener {
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<ReportedPeer> getConnectedPeersNonSeedNodes() {
|
private HashSet<ReportedPeer> getConnectedPeersNonSeedNodes() {
|
||||||
return new HashSet<>(getConnectedPeers().stream()
|
return new HashSet<>(getConnectedPeers().stream()
|
||||||
.filter(e -> !isSeedNode(e))
|
.filter(e -> !isSeedNode(e))
|
||||||
.collect(Collectors.toSet()));
|
.collect(Collectors.toSet()));
|
||||||
|
|
|
@ -243,7 +243,6 @@ public class RequestDataManager implements MessageListener {
|
||||||
!peerManager.isSelf(e))
|
!peerManager.isSelf(e))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
.stream()
|
.stream()
|
||||||
.filter(e -> e.date != null)
|
|
||||||
.sorted((o1, o2) -> o2.date.compareTo(o1.date))
|
.sorted((o1, o2) -> o2.date.compareTo(o1.date))
|
||||||
.map(e -> e.nodeAddress)
|
.map(e -> e.nodeAddress)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.bitsquare.p2p.peers.getdata.messages;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import io.bitsquare.app.Log;
|
import io.bitsquare.app.Log;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
import io.bitsquare.p2p.network.CloseConnectionReason;
|
import io.bitsquare.p2p.network.CloseConnectionReason;
|
||||||
import io.bitsquare.p2p.network.Connection;
|
import io.bitsquare.p2p.network.Connection;
|
||||||
import io.bitsquare.p2p.network.MessageListener;
|
import io.bitsquare.p2p.network.MessageListener;
|
||||||
|
@ -16,10 +17,13 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
class KeepAliveHandler implements MessageListener {
|
class KeepAliveHandler implements MessageListener {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeepAliveHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(KeepAliveHandler.class);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private Connection connection;
|
private Connection connection;
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,6 +35,8 @@ class KeepAliveHandler implements MessageListener {
|
||||||
void onComplete();
|
void onComplete();
|
||||||
|
|
||||||
void onFault(String errorMessage, Connection connection);
|
void onFault(String errorMessage, Connection connection);
|
||||||
|
|
||||||
|
void onFault(String errorMessage, NodeAddress nodeAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,6 +96,34 @@ class KeepAliveHandler implements MessageListener {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendPing(NodeAddress nodeAddress) {
|
||||||
|
Log.traceCall("nodeAddress=" + nodeAddress + " / this=" + this);
|
||||||
|
Ping ping = new Ping(nonce);
|
||||||
|
SettableFuture<Connection> future = networkNode.sendMessage(nodeAddress, ping);
|
||||||
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Connection connection) {
|
||||||
|
if (connection != null) {
|
||||||
|
KeepAliveHandler.this.connection = connection;
|
||||||
|
connection.addMessageListener(KeepAliveHandler.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.trace("Send " + ping + " to " + nodeAddress + " succeeded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
|
String errorMessage = "Sending ping to " + nodeAddress +
|
||||||
|
" failed. That is expected if the peer is offline.\n\tping=" + ping +
|
||||||
|
".\n\tException=" + throwable.getMessage();
|
||||||
|
log.info(errorMessage);
|
||||||
|
cleanup();
|
||||||
|
peerManager.shutDownConnection(nodeAddress, CloseConnectionReason.SEND_MSG_FAILURE);
|
||||||
|
listener.onFault(errorMessage, nodeAddress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// MessageListener implementation
|
// MessageListener implementation
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.bitsquare.app.Log;
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Utilities;
|
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.network.*;
|
import io.bitsquare.p2p.network.*;
|
||||||
import io.bitsquare.p2p.peers.PeerManager;
|
import io.bitsquare.p2p.peers.PeerManager;
|
||||||
import io.bitsquare.p2p.peers.keepalive.messages.Ping;
|
import io.bitsquare.p2p.peers.keepalive.messages.Ping;
|
||||||
|
@ -18,15 +19,14 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class KeepAliveManager implements MessageListener {
|
public class KeepAliveManager implements MessageListener {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeepAliveManager.class);
|
private static final Logger log = LoggerFactory.getLogger(KeepAliveManager.class);
|
||||||
|
|
||||||
//private static final int INTERVAL_SEC = new Random().nextInt(10) + 10;
|
private static final int INTERVAL_SEC = new Random().nextInt(10) + 10;
|
||||||
//TODO
|
|
||||||
private static final int INTERVAL_SEC = 3;
|
|
||||||
|
|
||||||
private final NetworkNode networkNode;
|
private final NetworkNode networkNode;
|
||||||
private final PeerManager peerManager;
|
private final PeerManager peerManager;
|
||||||
|
@ -115,23 +115,28 @@ public class KeepAliveManager implements MessageListener {
|
||||||
networkNode.getConfirmedConnections().stream()
|
networkNode.getConfirmedConnections().stream()
|
||||||
.filter(connection -> connection instanceof OutboundConnection)
|
.filter(connection -> connection instanceof OutboundConnection)
|
||||||
.forEach(connection -> {
|
.forEach(connection -> {
|
||||||
if (!maintenanceHandlerMap.containsKey(connection.getUid())) {
|
if (!maintenanceHandlerMap.containsKey(getKey(connection))) {
|
||||||
KeepAliveHandler keepAliveHandler = new KeepAliveHandler(networkNode, peerManager, new KeepAliveHandler.Listener() {
|
KeepAliveHandler keepAliveHandler = new KeepAliveHandler(networkNode, peerManager, new KeepAliveHandler.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
maintenanceHandlerMap.remove(connection.getUid());
|
maintenanceHandlerMap.remove(getKey(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFault(String errorMessage, Connection connection) {
|
public void onFault(String errorMessage, Connection connection) {
|
||||||
maintenanceHandlerMap.remove(connection.getUid());
|
maintenanceHandlerMap.remove(getKey(connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage, NodeAddress nodeAddress) {
|
||||||
|
maintenanceHandlerMap.remove(nodeAddress.getFullAddress());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
maintenanceHandlerMap.put(connection.getUid(), keepAliveHandler);
|
maintenanceHandlerMap.put(getKey(connection), keepAliveHandler);
|
||||||
keepAliveHandler.sendPing(connection);
|
keepAliveHandler.sendPing(connection);
|
||||||
} else {
|
} else {
|
||||||
log.warn("Connection with id {} has not completed and is still in our map. " +
|
log.warn("Connection with id {} has not completed and is still in our map. " +
|
||||||
"We will try to ping that peer at the next schedule.", connection.getUid());
|
"We will try to ping that peer at the next schedule.", getKey(connection));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -142,4 +147,14 @@ public class KeepAliveManager implements MessageListener {
|
||||||
"maintenanceHandlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
"maintenanceHandlerMap size={}, peerManager.getMaxConnections()={}", size, peerManager.getMaxConnections());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getKey(Connection connection) {
|
||||||
|
if (connection.getPeersNodeAddressOptional().isPresent()) {
|
||||||
|
return connection.getPeersNodeAddressOptional().get().getFullAddress();
|
||||||
|
} else {
|
||||||
|
// TODO not sure if that can be the case, but handle it otherwise we get an exception
|
||||||
|
log.warn("!connection.getPeersNodeAddressOptional().isPresent(). That should not happen.");
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ 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 RETRY_DELAY_SEC = 60;
|
||||||
|
private static final long RETRY_DELAY_AFTER_ALL_CON_LOST_SEC = 3;
|
||||||
private static final long REQUEST_PERIODICALLY_INTERVAL_MINUTES = 10;
|
private static final long REQUEST_PERIODICALLY_INTERVAL_MINUTES = 10;
|
||||||
|
|
||||||
private final NetworkNode networkNode;
|
private final NetworkNode networkNode;
|
||||||
|
@ -91,12 +92,18 @@ 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)
|
boolean lostAllConnections = networkNode.getAllConnections().isEmpty();
|
||||||
|
if (lostAllConnections || connectToMorePeersTimer == null) {
|
||||||
|
long delaySec = lostAllConnections ? RETRY_DELAY_AFTER_ALL_CON_LOST_SEC : RETRY_DELAY_SEC;
|
||||||
|
if (lostAllConnections && connectToMorePeersTimer != null)
|
||||||
|
connectToMorePeersTimer.cancel();
|
||||||
|
|
||||||
connectToMorePeersTimer = UserThread.runAfter(() -> {
|
connectToMorePeersTimer = UserThread.runAfter(() -> {
|
||||||
log.trace("ConnectToMorePeersTimer called from onDisconnect code path");
|
log.trace("ConnectToMorePeersTimer called from onDisconnect code path");
|
||||||
stopConnectToMorePeersTimer();
|
stopConnectToMorePeersTimer();
|
||||||
requestWithAvailablePeers();
|
requestWithAvailablePeers();
|
||||||
}, RETRY_DELAY_SEC);
|
}, delaySec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage;
|
package io.bitsquare.p2p.storage;
|
||||||
|
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
|
||||||
|
|
||||||
public interface HashMapChangedListener {
|
public interface HashMapChangedListener {
|
||||||
void onAdded(ProtectedData entry);
|
void onAdded(ProtectedData data);
|
||||||
|
|
||||||
void onRemoved(ProtectedData entry);
|
void onRemoved(ProtectedData data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,13 @@ import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.crypto.CryptoException;
|
import io.bitsquare.common.crypto.CryptoException;
|
||||||
import io.bitsquare.common.crypto.Hash;
|
import io.bitsquare.common.crypto.Hash;
|
||||||
import io.bitsquare.common.crypto.Sig;
|
import io.bitsquare.common.crypto.Sig;
|
||||||
|
import io.bitsquare.common.util.Tuple2;
|
||||||
import io.bitsquare.common.util.Utilities;
|
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.Broadcaster;
|
import io.bitsquare.p2p.peers.Broadcaster;
|
||||||
import io.bitsquare.p2p.storage.data.*;
|
import io.bitsquare.p2p.storage.messages.*;
|
||||||
import io.bitsquare.p2p.storage.messages.AddDataMessage;
|
|
||||||
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
|
|
||||||
import io.bitsquare.p2p.storage.messages.RemoveDataMessage;
|
|
||||||
import io.bitsquare.p2p.storage.messages.RemoveMailboxDataMessage;
|
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -28,7 +25,10 @@ import java.io.File;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
|
@ -39,14 +39,12 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
private static final Logger log = LoggerFactory.getLogger(P2PDataStorage.class);
|
private static final Logger log = LoggerFactory.getLogger(P2PDataStorage.class);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static int CHECK_TTL_INTERVAL_SEC = new Random().nextInt(60) + (int) TimeUnit.MINUTES.toSeconds(10); // 10-11 min.
|
public static int CHECK_TTL_INTERVAL_SEC = 30;
|
||||||
//TODO
|
|
||||||
// public static int CHECK_TTL_INTERVAL_SEC = 10;
|
|
||||||
|
|
||||||
private final Broadcaster broadcaster;
|
private final Broadcaster broadcaster;
|
||||||
private final Map<ByteArray, ProtectedData> map = new ConcurrentHashMap<>();
|
private final Map<ByteArray, ProtectedData> map = new ConcurrentHashMap<>();
|
||||||
private final CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<HashMapChangedListener> hashMapChangedListeners = new CopyOnWriteArraySet<>();
|
||||||
private HashMap<ByteArray, Integer> sequenceNumberMap = new HashMap<>();
|
private HashMap<ByteArray, Tuple2<Integer, Long>> sequenceNumberMap = new HashMap<>();
|
||||||
private final Storage<HashMap> storage;
|
private final Storage<HashMap> storage;
|
||||||
private final ScheduledThreadPoolExecutor removeExpiredEntriesExecutor;
|
private final ScheduledThreadPoolExecutor removeExpiredEntriesExecutor;
|
||||||
|
|
||||||
|
@ -63,41 +61,42 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
storage = new Storage<>(storageDir);
|
storage = new Storage<>(storageDir);
|
||||||
removeExpiredEntriesExecutor = Utilities.getScheduledThreadPoolExecutor("removeExpiredEntries", 1, 10, 5);
|
removeExpiredEntriesExecutor = Utilities.getScheduledThreadPoolExecutor("removeExpiredEntries", 1, 10, 5);
|
||||||
|
|
||||||
log.debug("CHECK_TTL_INTERVAL_SEC " + CHECK_TTL_INTERVAL_SEC);
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
HashMap<ByteArray, Integer> persisted = storage.initAndGetPersisted("SequenceNumberMap");
|
HashMap<ByteArray, Tuple2<Integer, Long>> persisted = storage.initAndGetPersisted("SequenceNumberMap");
|
||||||
if (persisted != null)
|
if (persisted != null)
|
||||||
sequenceNumberMap = persisted;
|
sequenceNumberMap = getPurgedSequenceNumberMap(persisted);
|
||||||
|
|
||||||
removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(this::removeExpiredEntries), CHECK_TTL_INTERVAL_SEC, CHECK_TTL_INTERVAL_SEC, TimeUnit.SECONDS);
|
removeExpiredEntriesExecutor.scheduleAtFixedRate(() -> UserThread.execute(() -> {
|
||||||
}
|
log.trace("removeExpiredEntries");
|
||||||
|
|
||||||
private void removeExpiredEntries() {
|
|
||||||
Log.traceCall();
|
|
||||||
// The moment when an object becomes expired will not be synchronous in the network and we could
|
// The moment when an object becomes expired will not be synchronous in the network and we could
|
||||||
// get add messages after the object has expired. To avoid repeated additions of already expired
|
// get add messages after the object has expired. To avoid repeated additions of already expired
|
||||||
// object when we get it sent from new peers, we don’t remove the sequence number from the map.
|
// object when we get it sent from new peers, we don’t remove the sequence number from the map.
|
||||||
// That way an ADD message for an already expired data will fail because the sequence number
|
// That way an ADD message for an already expired data will fail because the sequence number
|
||||||
// is equal and not larger.
|
// is equal and not larger.
|
||||||
Map<ByteArray, ProtectedData> temp = new HashMap<>(map);
|
Map<ByteArray, ProtectedData> temp = new HashMap<>(map);
|
||||||
Set<ProtectedData> protectedDataToRemoveSet = new HashSet<>();
|
Set<ProtectedData> toRemoveSet = new HashSet<>();
|
||||||
temp.entrySet().stream()
|
temp.entrySet().stream()
|
||||||
.filter(entry -> entry.getValue().isExpired())
|
.filter(entry -> entry.getValue().isExpired())
|
||||||
.forEach(entry -> {
|
.forEach(entry -> {
|
||||||
ByteArray hashOfPayload = entry.getKey();
|
ByteArray hashOfPayload = entry.getKey();
|
||||||
ProtectedData protectedDataToRemove = map.get(hashOfPayload);
|
toRemoveSet.add(map.get(hashOfPayload));
|
||||||
protectedDataToRemoveSet.add(protectedDataToRemove);
|
|
||||||
map.remove(hashOfPayload);
|
map.remove(hashOfPayload);
|
||||||
});
|
});
|
||||||
|
|
||||||
protectedDataToRemoveSet.stream().forEach(
|
toRemoveSet.stream().forEach(
|
||||||
protectedDataToRemove -> hashMapChangedListeners.stream().forEach(
|
protectedDataToRemove -> hashMapChangedListeners.stream().forEach(
|
||||||
listener -> listener.onRemoved(protectedDataToRemove)));
|
listener -> listener.onRemoved(protectedDataToRemove)));
|
||||||
|
|
||||||
|
if (sequenceNumberMap.size() > 1000)
|
||||||
|
sequenceNumberMap = getPurgedSequenceNumberMap(sequenceNumberMap);
|
||||||
|
|
||||||
|
}), CHECK_TTL_INTERVAL_SEC, CHECK_TTL_INTERVAL_SEC, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// MessageListener implementation
|
// MessageListener implementation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -119,65 +118,40 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ConnectionListener implementation
|
// ConnectionListener implementation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnection(Connection connection) {
|
public void onConnection(Connection connection) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
|
public void onDisconnect(CloseConnectionReason closeConnectionReason, Connection connection) {
|
||||||
Log.traceCall();
|
if (connection.getPeersNodeAddressOptional().isPresent()) {
|
||||||
map.values().stream()
|
map.values().stream()
|
||||||
.filter(protectedData -> protectedData.expirableMessage instanceof RequiresLiveOwner)
|
.forEach(protectedData -> {
|
||||||
.forEach(protectedData -> removeRequiresLiveOwnerDataOnDisconnect(protectedData, ((RequiresLiveOwner) protectedData.expirableMessage).getOwnerNodeAddress()));
|
ExpirableMessage expirableMessage = protectedData.expirableMessage;
|
||||||
}
|
if (expirableMessage instanceof RequiresLiveOwnerData) {
|
||||||
|
RequiresLiveOwnerData requiresLiveOwnerData = (RequiresLiveOwnerData) expirableMessage;
|
||||||
|
NodeAddress ownerNodeAddress = requiresLiveOwnerData.getOwnerNodeAddress();
|
||||||
|
if (ownerNodeAddress.equals(connection.getPeersNodeAddressOptional().get())) {
|
||||||
|
// We have a RequiresLiveOwnerData data object with the node address of the
|
||||||
|
// disconnected peer. We remove that data from our map.
|
||||||
|
|
||||||
public boolean removeRequiresLiveOwnerDataOnDisconnect(ProtectedData protectedData, NodeAddress owner) {
|
// Check if we have the data (e.g. Offer)
|
||||||
Log.traceCall();
|
|
||||||
ByteArray hashOfPayload = getHashAsByteArray(protectedData.expirableMessage);
|
|
||||||
boolean containsKey = map.containsKey(hashOfPayload);
|
|
||||||
if (containsKey) {
|
|
||||||
doRemoveProtectedExpirableData(protectedData, hashOfPayload);
|
|
||||||
|
|
||||||
//broadcast(new RemoveDataMessage(protectedData), owner);
|
|
||||||
|
|
||||||
// sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
|
|
||||||
sequenceNumberMap.remove(hashOfPayload);
|
|
||||||
storage.queueUpForSave(sequenceNumberMap, 5000);
|
|
||||||
} else {
|
|
||||||
log.debug("Remove data ignored as we don't have an entry for that data.");
|
|
||||||
}
|
|
||||||
return containsKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the data owner gets disconnected we remove his data. Used for offers to get clean up when the peer is in
|
|
||||||
// sleep/hibernate mode or closes the app without proper shutdown (crash).
|
|
||||||
// We don't want to wait the until the TTL period is over so we add that method to improve usability
|
|
||||||
public boolean removeLocalDataOnDisconnectedDataOwner(ExpirableMessage expirableMessage) {
|
|
||||||
Log.traceCall();
|
|
||||||
ByteArray hashOfPayload = getHashAsByteArray(expirableMessage);
|
ByteArray hashOfPayload = getHashAsByteArray(expirableMessage);
|
||||||
boolean containsKey = map.containsKey(hashOfPayload);
|
boolean containsKey = map.containsKey(hashOfPayload);
|
||||||
if (containsKey) {
|
if (containsKey) {
|
||||||
map.remove(hashOfPayload);
|
doRemoveProtectedExpirableData(protectedData, hashOfPayload);
|
||||||
log.trace("Data removed from our map.");
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n" +
|
|
||||||
"Data set after removeProtectedExpirableData: (truncated)");
|
|
||||||
map.values().stream().forEach(e -> sb.append("\n").append(StringUtils.abbreviate(e.toString(), 100)));
|
|
||||||
sb.append("\n------------------------------------------------------------\n");
|
|
||||||
log.trace(sb.toString());
|
|
||||||
log.info("Data set after addProtectedExpirableData: size=" + map.values().size());
|
|
||||||
|
|
||||||
// sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
|
|
||||||
// storage.queueUpForSave(sequenceNumberMap, 5000);
|
|
||||||
} else {
|
} else {
|
||||||
log.debug("Remove data ignored as we don't have an entry for that data.");
|
log.debug("Remove data ignored as we don't have an entry for that data.");
|
||||||
}
|
}
|
||||||
return containsKey;
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -207,6 +181,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
|
|
||||||
private boolean doAdd(ProtectedData protectedData, @Nullable NodeAddress sender, boolean rePublish) {
|
private boolean doAdd(ProtectedData protectedData, @Nullable NodeAddress sender, boolean rePublish) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
|
|
||||||
ByteArray hashOfPayload = getHashAsByteArray(protectedData.expirableMessage);
|
ByteArray hashOfPayload = getHashAsByteArray(protectedData.expirableMessage);
|
||||||
boolean result = checkPublicKeys(protectedData, true)
|
boolean result = checkPublicKeys(protectedData, true)
|
||||||
&& checkSignature(protectedData)
|
&& checkSignature(protectedData)
|
||||||
|
@ -222,10 +197,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
// Republished data have a larger sequence number. We set the rePublish flag to enable broadcasting
|
// Republished data have a larger sequence number. We set the rePublish flag to enable broadcasting
|
||||||
// even we had the data with the old seq nr. already
|
// even we had the data with the old seq nr. already
|
||||||
if (sequenceNumberMap.containsKey(hashOfPayload) &&
|
if (sequenceNumberMap.containsKey(hashOfPayload) &&
|
||||||
protectedData.sequenceNumber > sequenceNumberMap.get(hashOfPayload))
|
protectedData.sequenceNumber > sequenceNumberMap.get(hashOfPayload).first)
|
||||||
rePublish = true;
|
rePublish = true;
|
||||||
|
|
||||||
sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
|
sequenceNumberMap.put(hashOfPayload, new Tuple2<>(protectedData.sequenceNumber, System.currentTimeMillis()));
|
||||||
storage.queueUpForSave(sequenceNumberMap, 5000);
|
storage.queueUpForSave(sequenceNumberMap, 5000);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
|
StringBuilder sb = new StringBuilder("\n\n------------------------------------------------------------\n");
|
||||||
|
@ -262,7 +237,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
|
|
||||||
broadcast(new RemoveDataMessage(protectedData), sender);
|
broadcast(new RemoveDataMessage(protectedData), sender);
|
||||||
|
|
||||||
sequenceNumberMap.put(hashOfPayload, protectedData.sequenceNumber);
|
sequenceNumberMap.put(hashOfPayload, new Tuple2<>(protectedData.sequenceNumber, System.currentTimeMillis()));
|
||||||
storage.queueUpForSave(sequenceNumberMap, 5000);
|
storage.queueUpForSave(sequenceNumberMap, 5000);
|
||||||
} else {
|
} else {
|
||||||
log.debug("remove failed");
|
log.debug("remove failed");
|
||||||
|
@ -287,7 +262,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
|
|
||||||
broadcast(new RemoveMailboxDataMessage(protectedMailboxData), sender);
|
broadcast(new RemoveMailboxDataMessage(protectedMailboxData), sender);
|
||||||
|
|
||||||
sequenceNumberMap.put(hashOfData, protectedMailboxData.sequenceNumber);
|
sequenceNumberMap.put(hashOfData, new Tuple2<>(protectedMailboxData.sequenceNumber, System.currentTimeMillis()));
|
||||||
storage.queueUpForSave(sequenceNumberMap, 5000);
|
storage.queueUpForSave(sequenceNumberMap, 5000);
|
||||||
} else {
|
} else {
|
||||||
log.debug("removeMailboxData failed");
|
log.debug("removeMailboxData failed");
|
||||||
|
@ -302,11 +277,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
|
|
||||||
public ProtectedData getDataWithSignedSeqNr(ExpirableMessage payload, KeyPair ownerStoragePubKey)
|
public ProtectedData getDataWithSignedSeqNr(ExpirableMessage payload, KeyPair ownerStoragePubKey)
|
||||||
throws CryptoException {
|
throws CryptoException {
|
||||||
Log.traceCall();
|
|
||||||
ByteArray hashOfData = getHashAsByteArray(payload);
|
ByteArray hashOfData = getHashAsByteArray(payload);
|
||||||
int sequenceNumber;
|
int sequenceNumber;
|
||||||
if (sequenceNumberMap.containsKey(hashOfData))
|
if (sequenceNumberMap.containsKey(hashOfData))
|
||||||
sequenceNumber = sequenceNumberMap.get(hashOfData) + 1;
|
sequenceNumber = sequenceNumberMap.get(hashOfData).first + 1;
|
||||||
else
|
else
|
||||||
sequenceNumber = 0;
|
sequenceNumber = 0;
|
||||||
|
|
||||||
|
@ -318,11 +292,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
public ProtectedMailboxData getMailboxDataWithSignedSeqNr(MailboxMessage expirableMailboxPayload,
|
public ProtectedMailboxData getMailboxDataWithSignedSeqNr(MailboxMessage expirableMailboxPayload,
|
||||||
KeyPair storageSignaturePubKey, PublicKey receiversPublicKey)
|
KeyPair storageSignaturePubKey, PublicKey receiversPublicKey)
|
||||||
throws CryptoException {
|
throws CryptoException {
|
||||||
Log.traceCall();
|
|
||||||
ByteArray hashOfData = getHashAsByteArray(expirableMailboxPayload);
|
ByteArray hashOfData = getHashAsByteArray(expirableMailboxPayload);
|
||||||
int sequenceNumber;
|
int sequenceNumber;
|
||||||
if (sequenceNumberMap.containsKey(hashOfData))
|
if (sequenceNumberMap.containsKey(hashOfData))
|
||||||
sequenceNumber = sequenceNumberMap.get(hashOfData) + 1;
|
sequenceNumber = sequenceNumberMap.get(hashOfData).first + 1;
|
||||||
else
|
else
|
||||||
sequenceNumber = 0;
|
sequenceNumber = 0;
|
||||||
|
|
||||||
|
@ -342,7 +315,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void doRemoveProtectedExpirableData(ProtectedData protectedData, ByteArray hashOfPayload) {
|
private void doRemoveProtectedExpirableData(ProtectedData protectedData, ByteArray hashOfPayload) {
|
||||||
Log.traceCall();
|
|
||||||
map.remove(hashOfPayload);
|
map.remove(hashOfPayload);
|
||||||
log.trace("Data removed from our map. We broadcast the message to our peers.");
|
log.trace("Data removed from our map. We broadcast the message to our peers.");
|
||||||
hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedData));
|
hashMapChangedListeners.stream().forEach(e -> e.onRemoved(protectedData));
|
||||||
|
@ -356,20 +328,22 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSequenceNrValid(ProtectedData data, ByteArray hashOfData) {
|
private boolean isSequenceNrValid(ProtectedData data, ByteArray hashOfData) {
|
||||||
Log.traceCall();
|
|
||||||
int newSequenceNumber = data.sequenceNumber;
|
int newSequenceNumber = data.sequenceNumber;
|
||||||
Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData);
|
if (sequenceNumberMap.containsKey(hashOfData)) {
|
||||||
if (sequenceNumberMap.containsKey(hashOfData) && newSequenceNumber < storedSequenceNumber) {
|
Integer storedSequenceNumber = sequenceNumberMap.get(hashOfData).first;
|
||||||
log.trace("Sequence number is invalid. newSequenceNumber="
|
if (newSequenceNumber < storedSequenceNumber) {
|
||||||
|
log.warn("Sequence number is invalid. newSequenceNumber="
|
||||||
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber);
|
+ newSequenceNumber + " / storedSequenceNumber=" + storedSequenceNumber);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkSignature(ProtectedData data) {
|
private boolean checkSignature(ProtectedData data) {
|
||||||
Log.traceCall();
|
|
||||||
byte[] hashOfDataAndSeqNr = Hash.getHash(new DataAndSeqNrPair(data.expirableMessage, data.sequenceNumber));
|
byte[] hashOfDataAndSeqNr = Hash.getHash(new DataAndSeqNrPair(data.expirableMessage, data.sequenceNumber));
|
||||||
try {
|
try {
|
||||||
boolean result = Sig.verify(data.ownerPubKey, hashOfDataAndSeqNr, data.signature);
|
boolean result = Sig.verify(data.ownerPubKey, hashOfDataAndSeqNr, data.signature);
|
||||||
|
@ -385,7 +359,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPublicKeys(ProtectedData data, boolean isAddOperation) {
|
private boolean checkPublicKeys(ProtectedData data, boolean isAddOperation) {
|
||||||
Log.traceCall();
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
if (data.expirableMessage instanceof MailboxMessage) {
|
if (data.expirableMessage instanceof MailboxMessage) {
|
||||||
MailboxMessage expirableMailboxPayload = (MailboxMessage) data.expirableMessage;
|
MailboxMessage expirableMailboxPayload = (MailboxMessage) data.expirableMessage;
|
||||||
|
@ -403,7 +376,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfStoredDataPubKeyMatchesNewDataPubKey(ProtectedData data, ByteArray hashOfData) {
|
private boolean checkIfStoredDataPubKeyMatchesNewDataPubKey(ProtectedData data, ByteArray hashOfData) {
|
||||||
Log.traceCall();
|
|
||||||
ProtectedData storedData = map.get(hashOfData);
|
ProtectedData storedData = map.get(hashOfData);
|
||||||
boolean result = storedData.ownerPubKey.equals(data.ownerPubKey);
|
boolean result = storedData.ownerPubKey.equals(data.ownerPubKey);
|
||||||
if (!result)
|
if (!result)
|
||||||
|
@ -413,7 +385,6 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkIfStoredMailboxDataMatchesNewMailboxData(ProtectedMailboxData data, ByteArray hashOfData) {
|
private boolean checkIfStoredMailboxDataMatchesNewMailboxData(ProtectedMailboxData data, ByteArray hashOfData) {
|
||||||
Log.traceCall();
|
|
||||||
ProtectedData storedData = map.get(hashOfData);
|
ProtectedData storedData = map.get(hashOfData);
|
||||||
if (storedData instanceof ProtectedMailboxData) {
|
if (storedData instanceof ProtectedMailboxData) {
|
||||||
ProtectedMailboxData storedMailboxData = (ProtectedMailboxData) storedData;
|
ProtectedMailboxData storedMailboxData = (ProtectedMailboxData) storedData;
|
||||||
|
@ -434,8 +405,18 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||||
broadcaster.broadcast(message, sender);
|
broadcaster.broadcast(message, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteArray getHashAsByteArray(ExpirableMessage payload) {
|
private ByteArray getHashAsByteArray(ExpirableMessage data) {
|
||||||
return new ByteArray(Hash.getHash(payload));
|
return new ByteArray(Hash.getHash(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<ByteArray, Tuple2<Integer, Long>> getPurgedSequenceNumberMap(HashMap<ByteArray, Tuple2<Integer, Long>> persisted) {
|
||||||
|
HashMap<ByteArray, Tuple2<Integer, Long>> purged = new HashMap<>();
|
||||||
|
long maxAgeTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(10);
|
||||||
|
persisted.entrySet().stream().forEach(entry -> {
|
||||||
|
if (entry.getValue().second > maxAgeTs)
|
||||||
|
purged.put(entry.getKey(), entry.getValue());
|
||||||
|
});
|
||||||
|
return purged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import io.bitsquare.p2p.storage.P2PDataStorage;
|
import io.bitsquare.p2p.storage.messages.ExpirableMessage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage;
|
||||||
|
|
||||||
import io.bitsquare.p2p.storage.P2PDataStorage;
|
import io.bitsquare.p2p.storage.messages.MailboxMessage;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage.messages;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
|
|
||||||
public final class AddDataMessage extends DataBroadcastMessage {
|
public final class AddDataMessage extends DataBroadcastMessage {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.crypto.PrefixedSealedAndSignedMessage;
|
import io.bitsquare.crypto.PrefixedSealedAndSignedMessage;
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -30,7 +31,7 @@ public final class MailboxMessage implements ExpirableMessage {
|
||||||
* Used for check if the add operation is permitted.
|
* Used for check if the add operation is permitted.
|
||||||
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
||||||
*
|
*
|
||||||
* @see io.bitsquare.p2p.storage.data.ProtectedData#ownerPubKey
|
* @see ProtectedData#ownerPubKey
|
||||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedData, NodeAddress)
|
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedData, NodeAddress)
|
||||||
*/
|
*/
|
||||||
public final PublicKey senderPubKeyForAddOperation;
|
public final PublicKey senderPubKeyForAddOperation;
|
||||||
|
@ -39,7 +40,7 @@ public final class MailboxMessage implements ExpirableMessage {
|
||||||
* Used for check if the remove operation is permitted.
|
* Used for check if the remove operation is permitted.
|
||||||
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
* senderStoragePublicKey has to be equal to the ownerPubKey of the ProtectedData
|
||||||
*
|
*
|
||||||
* @see io.bitsquare.p2p.storage.data.ProtectedData#ownerPubKey
|
* @see ProtectedData#ownerPubKey
|
||||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedData, NodeAddress)
|
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedData, NodeAddress)
|
||||||
*/
|
*/
|
||||||
public final PublicKey receiverPubKeyForRemoveOperation;
|
public final PublicKey receiverPubKeyForRemoveOperation;
|
|
@ -1,7 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage.messages;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
|
|
||||||
public final class RemoveDataMessage extends DataBroadcastMessage {
|
public final class RemoveDataMessage extends DataBroadcastMessage {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage.messages;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
import io.bitsquare.p2p.storage.ProtectedMailboxData;
|
||||||
|
|
||||||
public final class RemoveMailboxDataMessage extends DataBroadcastMessage {
|
public final class RemoveMailboxDataMessage extends DataBroadcastMessage {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import java.io.Serializable;
|
||||||
* This is used for the offers to avoid dead offers in case the offerer is in sleep/hibernate mode or the app has
|
* This is used for the offers to avoid dead offers in case the offerer is in sleep/hibernate mode or the app has
|
||||||
* terminated without sending the remove message (e.g. in case of a crash).
|
* terminated without sending the remove message (e.g. in case of a crash).
|
||||||
*/
|
*/
|
||||||
public interface RequiresLiveOwner extends Serializable {
|
public interface RequiresLiveOwnerData extends Serializable {
|
||||||
/**
|
/**
|
||||||
* @return NodeAddress of the data owner
|
* @return NodeAddress of the data owner
|
||||||
*/
|
*/
|
|
@ -1,6 +1,7 @@
|
||||||
package io.bitsquare.p2p.storage.data;
|
package io.bitsquare.p2p.storage.messages;
|
||||||
|
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ public interface StorageMessage extends ExpirableMessage {
|
||||||
* OwnerPubKey has to be equal to the ownerPubKey of the ProtectedData
|
* OwnerPubKey has to be equal to the ownerPubKey of the ProtectedData
|
||||||
*
|
*
|
||||||
* @return The public key of the data owner.
|
* @return The public key of the data owner.
|
||||||
* @see io.bitsquare.p2p.storage.data.ProtectedData#ownerPubKey
|
* @see ProtectedData#ownerPubKey
|
||||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedData, NodeAddress)
|
* @see io.bitsquare.p2p.storage.P2PDataStorage#add(ProtectedData, NodeAddress)
|
||||||
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedData, NodeAddress)
|
* @see io.bitsquare.p2p.storage.P2PDataStorage#remove(ProtectedData, NodeAddress)
|
||||||
*/
|
*/
|
|
@ -11,7 +11,7 @@ import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
||||||
import io.bitsquare.p2p.peers.PeerManager;
|
import io.bitsquare.p2p.peers.PeerManager;
|
||||||
import io.bitsquare.p2p.seed.SeedNode;
|
import io.bitsquare.p2p.seed.SeedNode;
|
||||||
import io.bitsquare.p2p.storage.P2PDataStorage;
|
import io.bitsquare.p2p.storage.P2PDataStorage;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.ProtectedData;
|
||||||
import io.bitsquare.p2p.storage.mocks.MockData;
|
import io.bitsquare.p2p.storage.mocks.MockData;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
@ -25,6 +25,7 @@ import java.security.cert.CertificateException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
// TorNode created. Took 6 sec.
|
// TorNode created. Took 6 sec.
|
||||||
// Hidden service created. Took 40-50 sec.
|
// Hidden service created. Took 40-50 sec.
|
||||||
|
@ -157,7 +158,11 @@ public class P2PServiceTest {
|
||||||
Assert.assertEquals(1, p2PService3.getDataMap().size());
|
Assert.assertEquals(1, p2PService3.getDataMap().size());
|
||||||
|
|
||||||
// try to manipulate seq nr. -> fails
|
// try to manipulate seq nr. -> fails
|
||||||
ProtectedData origProtectedData = p2PService3.getDataMap().values().stream().findFirst().get();
|
Set<ProtectedData> dataSet = p2PService3.getDataMap().values().stream()
|
||||||
|
.filter(data -> data instanceof ProtectedData)
|
||||||
|
.map(data -> (ProtectedData) data)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
ProtectedData origProtectedData = dataSet.stream().findFirst().get();
|
||||||
ProtectedData protectedDataManipulated = new ProtectedData(origProtectedData.expirableMessage, origProtectedData.ttl, origProtectedData.ownerPubKey, origProtectedData.sequenceNumber + 1, origProtectedData.signature);
|
ProtectedData protectedDataManipulated = new ProtectedData(origProtectedData.expirableMessage, origProtectedData.ttl, origProtectedData.ownerPubKey, origProtectedData.sequenceNumber + 1, origProtectedData.signature);
|
||||||
Assert.assertFalse(p2PService3.removeData(protectedDataManipulated.expirableMessage));
|
Assert.assertFalse(p2PService3.removeData(protectedDataManipulated.expirableMessage));
|
||||||
Thread.sleep(sleepTime);
|
Thread.sleep(sleepTime);
|
||||||
|
|
|
@ -3,7 +3,7 @@ package io.bitsquare.p2p.mocks;
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.NodeAddress;
|
import io.bitsquare.p2p.NodeAddress;
|
||||||
import io.bitsquare.p2p.messaging.MailboxMessage;
|
import io.bitsquare.p2p.messaging.MailboxMessage;
|
||||||
import io.bitsquare.p2p.storage.data.ExpirableMessage;
|
import io.bitsquare.p2p.storage.messages.ExpirableMessage;
|
||||||
|
|
||||||
public final class MockMailboxMessage implements MailboxMessage, ExpirableMessage {
|
public final class MockMailboxMessage implements MailboxMessage, ExpirableMessage {
|
||||||
private final int messageVersion = Version.getP2PMessageVersion();
|
private final int messageVersion = Version.getP2PMessageVersion();
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.bitsquare.p2p.mocks;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.storage.data.ExpirableMessage;
|
import io.bitsquare.p2p.storage.messages.ExpirableMessage;
|
||||||
|
|
||||||
public final class MockMessage implements Message, ExpirableMessage {
|
public final class MockMessage implements Message, ExpirableMessage {
|
||||||
public final String msg;
|
public final String msg;
|
||||||
|
|
|
@ -11,9 +11,7 @@ import io.bitsquare.p2p.mocks.MockMessage;
|
||||||
import io.bitsquare.p2p.network.NetworkNode;
|
import io.bitsquare.p2p.network.NetworkNode;
|
||||||
import io.bitsquare.p2p.peers.Broadcaster;
|
import io.bitsquare.p2p.peers.Broadcaster;
|
||||||
import io.bitsquare.p2p.peers.PeerManager;
|
import io.bitsquare.p2p.peers.PeerManager;
|
||||||
import io.bitsquare.p2p.storage.data.MailboxMessage;
|
import io.bitsquare.p2p.storage.messages.MailboxMessage;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
|
||||||
import io.bitsquare.p2p.storage.mocks.MockData;
|
import io.bitsquare.p2p.storage.mocks.MockData;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package io.bitsquare.p2p.storage.mocks;
|
package io.bitsquare.p2p.storage.mocks;
|
||||||
|
|
||||||
import io.bitsquare.p2p.storage.data.StorageMessage;
|
import io.bitsquare.p2p.storage.messages.StorageMessage;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package io.bitsquare.p2p.seed;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import io.bitsquare.app.BitsquareEnvironment;
|
import io.bitsquare.app.BitsquareEnvironment;
|
||||||
import io.bitsquare.common.UserThread;
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.trade.offer.OfferBookService;
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -15,8 +14,7 @@ import java.util.concurrent.ThreadFactory;
|
||||||
public class SeedNodeMain {
|
public class SeedNodeMain {
|
||||||
private static final Logger log = LoggerFactory.getLogger(SeedNodeMain.class);
|
private static final Logger log = LoggerFactory.getLogger(SeedNodeMain.class);
|
||||||
|
|
||||||
public static final boolean USE_DETAILED_LOGGING = true;
|
private static final boolean USE_DETAILED_LOGGING = true;
|
||||||
private OfferBookService offerBookService;
|
|
||||||
|
|
||||||
private SeedNode seedNode;
|
private SeedNode seedNode;
|
||||||
|
|
||||||
|
@ -26,7 +24,7 @@ public class SeedNodeMain {
|
||||||
new SeedNodeMain(args);
|
new SeedNodeMain(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SeedNodeMain(String[] args) throws InterruptedException {
|
private SeedNodeMain(String[] args) throws InterruptedException {
|
||||||
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
|
||||||
.setNameFormat("SeedNodeMain")
|
.setNameFormat("SeedNodeMain")
|
||||||
.setDaemon(true)
|
.setDaemon(true)
|
||||||
|
@ -50,10 +48,6 @@ public class SeedNodeMain {
|
||||||
seedNode = new SeedNode(BitsquareEnvironment.defaultUserDataDir());
|
seedNode = new SeedNode(BitsquareEnvironment.defaultUserDataDir());
|
||||||
seedNode.processArgs(args);
|
seedNode.processArgs(args);
|
||||||
seedNode.createAndStartP2PService(USE_DETAILED_LOGGING);
|
seedNode.createAndStartP2PService(USE_DETAILED_LOGGING);
|
||||||
|
|
||||||
// We need the offerbook service to handle the case when the offerer is in sleep/hibernate mode and
|
|
||||||
// we want to remove his offers and not wait until TTL is over.
|
|
||||||
offerBookService = new OfferBookService(seedNode.getSeedNodeP2PService());
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.error("Executing task failed. " + t.getMessage());
|
log.error("Executing task failed. " + t.getMessage());
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue