diff --git a/common/pom.xml b/common/pom.xml
index 96abceb58e3..4d637ab0707 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -5,7 +5,7 @@
parent
io.bitsquare
- 0.4.9.2
+ 0.4.9.3
4.0.0
diff --git a/common/src/main/java/io/bitsquare/app/Version.java b/common/src/main/java/io/bitsquare/app/Version.java
index d9e31bd348e..ce3d4d70096 100644
--- a/common/src/main/java/io/bitsquare/app/Version.java
+++ b/common/src/main/java/io/bitsquare/app/Version.java
@@ -24,7 +24,7 @@ public class Version {
private static final Logger log = LoggerFactory.getLogger(Version.class);
// The application versions
- public static final String VERSION = "0.4.9.2";
+ public static final String VERSION = "0.4.9.3";
// The version nr. for the objects sent over the network. A change will break the serialization of old objects.
// If objects are used for both network and database the network version is applied.
diff --git a/common/src/main/java/io/bitsquare/common/crypto/Hash.java b/common/src/main/java/io/bitsquare/common/crypto/Hash.java
index 8a780d153e9..0a08679ccc0 100644
--- a/common/src/main/java/io/bitsquare/common/crypto/Hash.java
+++ b/common/src/main/java/io/bitsquare/common/crypto/Hash.java
@@ -37,15 +37,14 @@ public class Hash {
* @return Hash of data
*/
public static byte[] getHash(byte[] data) {
- MessageDigest digest;
try {
- digest = MessageDigest.getInstance("SHA-256", "BC");
+ MessageDigest digest = MessageDigest.getInstance("SHA-256", "BC");
+ digest.update(data, 0, data.length);
+ return digest.digest();
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Could not create MessageDigest for hash. " + e.getMessage());
throw new RuntimeException(e);
}
- digest.update(data, 0, data.length);
- return digest.digest();
}
/**
diff --git a/common/src/main/java/io/bitsquare/common/crypto/KeyStorage.java b/common/src/main/java/io/bitsquare/common/crypto/KeyStorage.java
index ee25b622b26..75304d5ba9d 100644
--- a/common/src/main/java/io/bitsquare/common/crypto/KeyStorage.java
+++ b/common/src/main/java/io/bitsquare/common/crypto/KeyStorage.java
@@ -88,7 +88,7 @@ private boolean fileExists(KeyEntry keyEntry) {
}
public KeyPair loadKeyPair(KeyEntry keyEntry) {
- FileUtil.rollingBackup(storageDir, keyEntry.getFileName() + ".key");
+ FileUtil.rollingBackup(storageDir, keyEntry.getFileName() + ".key", 20);
// long now = System.currentTimeMillis();
try {
KeyFactory keyFactory = KeyFactory.getInstance(keyEntry.getAlgorithm(), "BC");
diff --git a/common/src/main/java/io/bitsquare/storage/FileManager.java b/common/src/main/java/io/bitsquare/storage/FileManager.java
index 6dbcce6f712..2e7b96da6ce 100644
--- a/common/src/main/java/io/bitsquare/storage/FileManager.java
+++ b/common/src/main/java/io/bitsquare/storage/FileManager.java
@@ -152,8 +152,8 @@ public synchronized void removeAndBackupFile(String fileName) throws IOException
renameTempFileToFile(storageFile, corruptedFile);
}
- public synchronized void backupFile(String fileName) throws IOException {
- FileUtil.rollingBackup(dir, fileName);
+ public synchronized void backupFile(String fileName, int numMaxBackupFiles) throws IOException {
+ FileUtil.rollingBackup(dir, fileName, numMaxBackupFiles);
}
///////////////////////////////////////////////////////////////////////////////////////////
@@ -171,30 +171,36 @@ private synchronized void saveToFile(T serializable, File dir, File storageFile)
File tempFile = null;
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
+ PrintWriter printWriter = null;
try {
if (!dir.exists())
if (!dir.mkdir())
log.warn("make dir failed");
tempFile = File.createTempFile("temp", null, dir);
-
- // Don't use auto closeable resources in try() as we would need too many try/catch clauses (for tempFile)
- // and we need to close it
- // manually before replacing file with temp file
- fileOutputStream = new FileOutputStream(tempFile);
- objectOutputStream = new ObjectOutputStream(fileOutputStream);
-
- objectOutputStream.writeObject(serializable);
- // Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
- // to not write through to physical media for at least a few seconds, but this is the best we can do.
- fileOutputStream.flush();
- fileOutputStream.getFD().sync();
-
- // Close resources before replacing file with temp file because otherwise it causes problems on windows
- // when rename temp file
- fileOutputStream.close();
- objectOutputStream.close();
-
+ tempFile.deleteOnExit();
+ if (serializable instanceof PlainTextWrapper) {
+ // When we dump json files we don't want to safe it as java serialized string objects, so we use PrintWriter instead.
+ printWriter = new PrintWriter(tempFile);
+ printWriter.println(((PlainTextWrapper) serializable).plainText);
+ } else {
+ // Don't use auto closeable resources in try() as we would need too many try/catch clauses (for tempFile)
+ // and we need to close it
+ // manually before replacing file with temp file
+ fileOutputStream = new FileOutputStream(tempFile);
+ objectOutputStream = new ObjectOutputStream(fileOutputStream);
+
+ objectOutputStream.writeObject(serializable);
+ // Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
+ // to not write through to physical media for at least a few seconds, but this is the best we can do.
+ fileOutputStream.flush();
+ fileOutputStream.getFD().sync();
+
+ // Close resources before replacing file with temp file because otherwise it causes problems on windows
+ // when rename temp file
+ fileOutputStream.close();
+ objectOutputStream.close();
+ }
renameTempFileToFile(tempFile, storageFile);
} catch (Throwable t) {
log.error("storageFile " + storageFile.toString());
@@ -212,6 +218,8 @@ private synchronized void saveToFile(T serializable, File dir, File storageFile)
objectOutputStream.close();
if (fileOutputStream != null)
fileOutputStream.close();
+ if (printWriter != null)
+ printWriter.close();
} catch (IOException e) {
// We swallow that
e.printStackTrace();
diff --git a/common/src/main/java/io/bitsquare/storage/FileUtil.java b/common/src/main/java/io/bitsquare/storage/FileUtil.java
index 84040b3449b..822e18598fc 100644
--- a/common/src/main/java/io/bitsquare/storage/FileUtil.java
+++ b/common/src/main/java/io/bitsquare/storage/FileUtil.java
@@ -4,9 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+import java.io.*;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Date;
@@ -14,10 +12,8 @@
public class FileUtil {
private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
- /** Number of copies to keep in backup directory. */
- private static final int KEPT_BACKUPS = 10;
- public static void rollingBackup(File dir, String fileName) {
+ public static void rollingBackup(File dir, String fileName, int numMaxBackupFiles) {
if (dir.exists()) {
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
if (!backupDir.exists())
@@ -39,7 +35,7 @@ public static void rollingBackup(File dir, String fileName) {
try {
Files.copy(origFile, backupFile);
- pruneBackup(backupFileDir);
+ pruneBackup(backupFileDir, numMaxBackupFiles);
} catch (IOException e) {
log.error("Backup key failed: " + e.getMessage());
e.printStackTrace();
@@ -48,22 +44,22 @@ public static void rollingBackup(File dir, String fileName) {
}
}
- private static void pruneBackup(File backupDir) {
+ private static void pruneBackup(File backupDir, int numMaxBackupFiles) {
if (backupDir.isDirectory()) {
File[] files = backupDir.listFiles();
if (files != null) {
List filesList = Arrays.asList(files);
- if (filesList.size() > KEPT_BACKUPS) {
+ if (filesList.size() > numMaxBackupFiles) {
filesList.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
File file = filesList.get(0);
if (file.isFile()) {
if (!file.delete())
log.error("Failed to delete file: " + file);
else
- pruneBackup(backupDir);
+ pruneBackup(backupDir, numMaxBackupFiles);
} else {
- pruneBackup(new File(Paths.get(backupDir.getAbsolutePath(), file.getName()).toString()));
+ pruneBackup(new File(Paths.get(backupDir.getAbsolutePath(), file.getName()).toString()), numMaxBackupFiles);
}
}
}
@@ -80,4 +76,20 @@ public static void deleteDirectory(File file) throws IOException {
if (file.exists() && !file.delete())
throw new FileNotFoundException("Failed to delete file: " + file);
}
+
+ public static void resourceToFile(String resourcePath, File destinationFile) throws ResourceNotFoundException, IOException {
+ InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
+ if (inputStream == null)
+ throw new ResourceNotFoundException(resourcePath);
+
+ try (FileOutputStream fileOutputStream = new FileOutputStream(destinationFile)) {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ fileOutputStream.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ throw e;
+ }
+ }
}
diff --git a/common/src/main/java/io/bitsquare/storage/PlainTextWrapper.java b/common/src/main/java/io/bitsquare/storage/PlainTextWrapper.java
new file mode 100644
index 00000000000..c5414ffa1d7
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/storage/PlainTextWrapper.java
@@ -0,0 +1,18 @@
+package io.bitsquare.storage;
+
+import java.io.Serializable;
+
+/**
+ * Used to wrap a plaintext string to distinguish at file storage and safe it as plain text instead of a serialized java object.
+ */
+public class PlainTextWrapper implements Serializable {
+ // That object is not saved to disc it is only of type Serializable to support the persistent framework.
+ // SerialVersionUID has no relevance here.
+ private static final long serialVersionUID = 0;
+
+ public final String plainText;
+
+ public PlainTextWrapper(String plainText) {
+ this.plainText = plainText;
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/storage/ResourceNotFoundException.java b/common/src/main/java/io/bitsquare/storage/ResourceNotFoundException.java
new file mode 100644
index 00000000000..6b77fef71da
--- /dev/null
+++ b/common/src/main/java/io/bitsquare/storage/ResourceNotFoundException.java
@@ -0,0 +1,7 @@
+package io.bitsquare.storage;
+
+public class ResourceNotFoundException extends Exception {
+ public ResourceNotFoundException(String path) {
+ super("Resource not found: path = " + path);
+ }
+}
diff --git a/common/src/main/java/io/bitsquare/storage/Storage.java b/common/src/main/java/io/bitsquare/storage/Storage.java
index 4621144e79d..93909b20ca3 100644
--- a/common/src/main/java/io/bitsquare/storage/Storage.java
+++ b/common/src/main/java/io/bitsquare/storage/Storage.java
@@ -65,6 +65,7 @@ public interface DataBaseCorruptionHandler {
private File storageFile;
private T serializable;
private String fileName;
+ private int numMaxBackupFiles = 10;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -76,6 +77,13 @@ public Storage(@Named(DIR_KEY) File dir) {
this.dir = dir;
}
+ @Nullable
+ public void initWithFileName(String fileName) {
+ this.fileName = fileName;
+ storageFile = new File(dir, fileName);
+ fileManager = new FileManager<>(dir, storageFile, 300);
+ }
+
@Nullable
public T initAndGetPersistedWithFileName(String fileName) {
this.fileName = fileName;
@@ -108,6 +116,10 @@ public void queueUpForSave(long delayInMilli) {
queueUpForSave(serializable, delayInMilli);
}
+ public void setNumMaxBackupFiles(int numMaxBackupFiles) {
+ this.numMaxBackupFiles = numMaxBackupFiles;
+ }
+
// Save delayed and on a background thread
private void queueUpForSave(T serializable) {
if (serializable != null) {
@@ -152,7 +164,7 @@ private T getPersisted() {
// If we did not get any exception we can be sure the data are consistent so we make a backup
now = System.currentTimeMillis();
- fileManager.backupFile(fileName);
+ fileManager.backupFile(fileName, numMaxBackupFiles);
log.trace("Backup {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
return persistedObject;
diff --git a/core/pom.xml b/core/pom.xml
index fa34ab2e2b1..a8d1e511201 100755
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,7 +6,7 @@
parent
io.bitsquare
- 0.4.9.2
+ 0.4.9.3
core
diff --git a/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java b/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java
index bbf180a62e9..2ecea5b5078 100644
--- a/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java
+++ b/core/src/main/java/io/bitsquare/app/BitsquareEnvironment.java
@@ -79,7 +79,7 @@ public static void setDefaultAppName(String defaultAppName) {
private final String logLevel;
private BitcoinNetwork bitcoinNetwork;
private final String btcSeedNodes, seedNodes, ignoreDevMsg, useTorForBtc, useTorForHttp,
- myAddress, banList, dumpStatistics, socks5ProxyBtcAddress, socks5ProxyHttpAddress;
+ myAddress, banList, dumpStatistics, maxMemory, socks5ProxyBtcAddress, socks5ProxyHttpAddress;
public BitsquareEnvironment(OptionSet options) {
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
@@ -141,7 +141,10 @@ protected BitsquareEnvironment(PropertySource commandLineProperties) {
dumpStatistics = commandLineProperties.containsProperty(CoreOptionKeys.DUMP_STATISTICS) ?
(String) commandLineProperties.getProperty(CoreOptionKeys.DUMP_STATISTICS) :
"";
-
+ maxMemory = commandLineProperties.containsProperty(CoreOptionKeys.MAX_MEMORY) ?
+ (String) commandLineProperties.getProperty(CoreOptionKeys.MAX_MEMORY) :
+ "";
+
seedNodes = commandLineProperties.containsProperty(NetworkOptionKeys.SEED_NODES_KEY) ?
(String) commandLineProperties.getProperty(NetworkOptionKeys.SEED_NODES_KEY) :
"";
@@ -239,6 +242,7 @@ private PropertySource> defaultProperties() {
setProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
setProperty(CoreOptionKeys.DUMP_STATISTICS, dumpStatistics);
setProperty(CoreOptionKeys.APP_NAME_KEY, appName);
+ setProperty(CoreOptionKeys.MAX_MEMORY, maxMemory);
setProperty(CoreOptionKeys.USER_DATA_DIR_KEY, userDataDir);
setProperty(BtcOptionKeys.BTC_SEED_NODES, btcSeedNodes);
diff --git a/core/src/main/java/io/bitsquare/app/BitsquareExecutable.java b/core/src/main/java/io/bitsquare/app/BitsquareExecutable.java
index fd52ad4c66b..694a16a4514 100644
--- a/core/src/main/java/io/bitsquare/app/BitsquareExecutable.java
+++ b/core/src/main/java/io/bitsquare/app/BitsquareExecutable.java
@@ -100,6 +100,8 @@ protected void customizeOptionParsing(OptionParser parser) {
.withRequiredArg();
parser.accepts(CoreOptionKeys.APP_NAME_KEY, description("Application name", DEFAULT_APP_NAME))
.withRequiredArg();
+ parser.accepts(CoreOptionKeys.MAX_MEMORY, description("Max. permitted memory (used only at headless versions)", 600))
+ .withRequiredArg();
parser.accepts(CoreOptionKeys.APP_DATA_DIR_KEY, description("Application data directory", DEFAULT_APP_DATA_DIR))
.withRequiredArg();
parser.accepts(CoreOptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " +
diff --git a/core/src/main/java/io/bitsquare/app/CoreOptionKeys.java b/core/src/main/java/io/bitsquare/app/CoreOptionKeys.java
index 4e24b8c65b6..6df92e3bff0 100644
--- a/core/src/main/java/io/bitsquare/app/CoreOptionKeys.java
+++ b/core/src/main/java/io/bitsquare/app/CoreOptionKeys.java
@@ -6,4 +6,5 @@ public class CoreOptionKeys {
public static final String USER_DATA_DIR_KEY = "userDataDir";
public static final String APP_NAME_KEY = "appName";
public static final String APP_DATA_DIR_KEY = "appDataDir";
+ public static final String MAX_MEMORY = "maxMemory";
}
diff --git a/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java b/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java
index 84fb8d5c6a9..a7a059023b2 100644
--- a/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java
+++ b/core/src/main/java/io/bitsquare/arbitration/DisputeResult.java
@@ -57,7 +57,8 @@ public enum Reason {
public final String tradeId;
public final int traderId;
private DisputeFeePolicy disputeFeePolicy;
-
+ private Winner winner;
+ private Reason reason;
private boolean tamperProofEvidence;
private boolean idVerification;
private boolean screenCast;
@@ -70,8 +71,6 @@ public enum Reason {
private String arbitratorAddressAsString;
private byte[] arbitratorPubKey;
private long closeDate;
- private Winner winner;
- private Reason reason;
transient private BooleanProperty tamperProofEvidenceProperty = new SimpleBooleanProperty();
transient private BooleanProperty idVerificationProperty = new SimpleBooleanProperty();
@@ -251,6 +250,22 @@ public boolean equals(Object o) {
if (tradeId != null ? !tradeId.equals(that.tradeId) : that.tradeId != null) return false;
if (disputeFeePolicy != that.disputeFeePolicy) return false;
if (reason != that.reason) return false;
+
+ if (disputeFeePolicy != null && that.disputeFeePolicy != null && disputeFeePolicy.ordinal() != that.disputeFeePolicy.ordinal())
+ return false;
+ else if ((disputeFeePolicy == null && that.disputeFeePolicy != null) || (disputeFeePolicy != null && that.disputeFeePolicy == null))
+ return false;
+
+ if (reason != null && that.reason != null && reason.ordinal() != that.reason.ordinal())
+ return false;
+ else if ((reason == null && that.reason != null) || (reason != null && that.reason == null))
+ return false;
+
+ if (winner != null && that.winner != null && winner.ordinal() != that.winner.ordinal())
+ return false;
+ else if ((winner == null && that.winner != null) || (winner != null && that.winner == null))
+ return false;
+
if (summaryNotes != null ? !summaryNotes.equals(that.summaryNotes) : that.summaryNotes != null) return false;
if (disputeCommunicationMessage != null ? !disputeCommunicationMessage.equals(that.disputeCommunicationMessage) : that.disputeCommunicationMessage != null)
return false;
@@ -258,7 +273,7 @@ public boolean equals(Object o) {
if (arbitratorAddressAsString != null ? !arbitratorAddressAsString.equals(that.arbitratorAddressAsString) : that.arbitratorAddressAsString != null)
return false;
if (!Arrays.equals(arbitratorPubKey, that.arbitratorPubKey)) return false;
- return winner == that.winner;
+ return true;
}
@@ -266,8 +281,9 @@ public boolean equals(Object o) {
public int hashCode() {
int result = tradeId != null ? tradeId.hashCode() : 0;
result = 31 * result + traderId;
- result = 31 * result + (disputeFeePolicy != null ? disputeFeePolicy.hashCode() : 0);
- result = 31 * result + (reason != null ? reason.hashCode() : 0);
+ result = 31 * result + (disputeFeePolicy != null ? disputeFeePolicy.ordinal() : 0);
+ result = 31 * result + (reason != null ? reason.ordinal() : 0);
+ result = 31 * result + (winner != null ? winner.ordinal() : 0);
result = 31 * result + (tamperProofEvidence ? 1 : 0);
result = 31 * result + (idVerification ? 1 : 0);
result = 31 * result + (screenCast ? 1 : 0);
@@ -280,7 +296,6 @@ public int hashCode() {
result = 31 * result + (arbitratorAddressAsString != null ? arbitratorAddressAsString.hashCode() : 0);
result = 31 * result + (arbitratorPubKey != null ? Arrays.hashCode(arbitratorPubKey) : 0);
result = 31 * result + (int) (closeDate ^ (closeDate >>> 32));
- result = 31 * result + (winner != null ? winner.hashCode() : 0);
return result;
}
}
diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java
index aa3b0d1c105..b72b9f958a9 100644
--- a/core/src/main/java/io/bitsquare/btc/WalletService.java
+++ b/core/src/main/java/io/bitsquare/btc/WalletService.java
@@ -410,7 +410,7 @@ public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler
}
public void backupWallet() {
- FileUtil.rollingBackup(walletDir, "Bitsquare.wallet");
+ FileUtil.rollingBackup(walletDir, "Bitsquare.wallet", 20);
}
public void clearBackup() {
diff --git a/core/src/main/java/io/bitsquare/locale/BankUtil.java b/core/src/main/java/io/bitsquare/locale/BankUtil.java
index fcecabc5e2f..bbeef006d59 100644
--- a/core/src/main/java/io/bitsquare/locale/BankUtil.java
+++ b/core/src/main/java/io/bitsquare/locale/BankUtil.java
@@ -52,7 +52,7 @@ public static String getBankNameLabel(String countryCode) {
case "BR":
return "Bank name:";
default:
- return "Bank name (optional):";
+ return isBankNameRequired(countryCode) ? "Bank name:" : "Bank name (optional):";
}
}
@@ -83,7 +83,7 @@ public static String getBankIdLabel(String countryCode) {
case "HK":
return "Bank code:";
default:
- return "Bank ID (e.g. BIC or SWIFT) (optional):";
+ return isBankIdRequired(countryCode) ? "Bank ID (e.g. BIC or SWIFT):" : "Bank ID (e.g. BIC or SWIFT) (optional):";
}
}
@@ -120,7 +120,7 @@ public static String getBranchIdLabel(String countryCode) {
case "CA":
return "Transit Number:";
default:
- return "Branch nr. (optional):";
+ return isBranchIdRequired(countryCode) ? "Branch nr.:" : "Branch nr. (optional):";
}
}
diff --git a/core/src/main/java/io/bitsquare/locale/CurrencyTuple.java b/core/src/main/java/io/bitsquare/locale/CurrencyTuple.java
index a124acd7b3c..350f98b0b09 100644
--- a/core/src/main/java/io/bitsquare/locale/CurrencyTuple.java
+++ b/core/src/main/java/io/bitsquare/locale/CurrencyTuple.java
@@ -24,9 +24,17 @@ public class CurrencyTuple implements Serializable {
public final String code;
public final String name;
+ public final int precision; // precision 4 is 1/10000 -> 0.0001 is smallest unit
public CurrencyTuple(String code, String name) {
+ // We use Fiat class and there precision is 4
+ // In future we might add custom precision per currency
+ this(code, name, 4);
+ }
+
+ public CurrencyTuple(String code, String name, int precision) {
this.code = code;
this.name = name;
+ this.precision = precision;
}
}
diff --git a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java
index 4bbcb101d04..d488bc10144 100644
--- a/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java
+++ b/core/src/main/java/io/bitsquare/locale/CurrencyUtil.java
@@ -77,7 +77,9 @@ public static List createAllSortedCryptoCurrenciesList() {
result.add(new CryptoCurrency("ETH", "Ether"));
result.add(new CryptoCurrency("ETC", "EtherClassic"));
result.add(new CryptoCurrency("STEEM", "STEEM"));
+ result.add(new CryptoCurrency("STEEMUSD", "Steem Dollars", true));
result.add(new CryptoCurrency("FLO", "FlorinCoin"));
+ result.add(new CryptoCurrency("MT", "Mycelium Token", true));
result.add(new CryptoCurrency("XEM", "NEM"));
result.add(new CryptoCurrency("LTC", "Litecoin"));
result.add(new CryptoCurrency("DASH", "Dash"));
@@ -85,6 +87,17 @@ public static List createAllSortedCryptoCurrenciesList() {
result.add(new CryptoCurrency("NBT", "NuBits"));
result.add(new CryptoCurrency("NSR", "NuShares"));
result.add(new CryptoCurrency("SDC", "ShadowCash"));
+ result.add(new CryptoCurrency("BTS", "BitShares"));
+ result.add(new CryptoCurrency("BITUSD", "BitUSD", true));
+ result.add(new CryptoCurrency("BITEUR", "BitEUR", true));
+ result.add(new CryptoCurrency("BITCNY", "BitCNY", true));
+ result.add(new CryptoCurrency("BITCHF", "BitCHF", true));
+ result.add(new CryptoCurrency("BITGBP", "BitGBP", true));
+ result.add(new CryptoCurrency("BITNZD", "BitNZD", true));
+ result.add(new CryptoCurrency("BITAUD", "BitAUD", true));
+ result.add(new CryptoCurrency("BITSGD", "BitSGD", true));
+ result.add(new CryptoCurrency("BITHKD", "BitHKD", true));
+ result.add(new CryptoCurrency("BITSEK", "BitSEK", true));
result.add(new CryptoCurrency("PPC", "Peercoin"));
result.add(new CryptoCurrency("XPM", "Primecoin"));
result.add(new CryptoCurrency("SJCX", "StorjcoinX"));
@@ -93,7 +106,6 @@ public static List createAllSortedCryptoCurrenciesList() {
result.add(new CryptoCurrency("BLK", "Blackcoin"));
result.add(new CryptoCurrency("FCT", "Factom"));
result.add(new CryptoCurrency("NXT", "Nxt"));
- result.add(new CryptoCurrency("BTS", "BitShares"));
result.add(new CryptoCurrency("XCP", "Counterparty"));
result.add(new CryptoCurrency("XRP", "Ripple"));
result.add(new CryptoCurrency("FAIR", "FairCoin"));
@@ -131,7 +143,8 @@ public static List createAllSortedCryptoCurrenciesList() {
result.add(new CryptoCurrency("JPYT", "JPY Tether"));
result.add(new CryptoCurrency("WDC", "Worldcoin"));
result.add(new CryptoCurrency("DAO", "DAO", true));
- result.add(new CryptoCurrency("ETHC", "EtherClassic (deprecated ticker symbol)"));
+ result.add(new CryptoCurrency("CMT", "Comet"));
+ result.add(new CryptoCurrency("SYNQ", "BitSYNQ"));
return result;
}
@@ -142,11 +155,14 @@ public static List getMainCryptoCurrencies() {
result.add(new CryptoCurrency("ETH", "Ether"));
result.add(new CryptoCurrency("ETC", "EtherClassic"));
result.add(new CryptoCurrency("STEEM", "STEEM"));
+ result.add(new CryptoCurrency("MT", "Mycelium Token", true));
result.add(new CryptoCurrency("FLO", "FlorinCoin"));
result.add(new CryptoCurrency("LTC", "Litecoin"));
result.add(new CryptoCurrency("DASH", "Dash"));
result.add(new CryptoCurrency("NMC", "Namecoin"));
result.add(new CryptoCurrency("NBT", "NuBits"));
+ result.add(new CryptoCurrency("BITUSD", "BitUSD", true));
+ result.add(new CryptoCurrency("STEEMUSD", "Steem Dollars", true));
result.add(new CryptoCurrency("DOGE", "Dogecoin"));
result.add(new CryptoCurrency("NXT", "Nxt"));
result.add(new CryptoCurrency("BTS", "BitShares"));
@@ -237,11 +253,15 @@ public static String getNameByCode(String currencyCode) {
try {
return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale());
} catch (Throwable t) {
- log.warn("No currency name available " + t.getMessage());
- return "N/A (" + currencyCode + ")";
+ log.debug("No currency name available " + t.getMessage());
+ return currencyCode;
}
}
+ public static String getNameAndCode(String currencyCode) {
+ return getNameByCode(currencyCode) + " (" + currencyCode + ")";
+ }
+
public static TradeCurrency getDefaultTradeCurrency() {
return Preferences.getDefaultTradeCurrency();
}
diff --git a/core/src/main/java/io/bitsquare/payment/CryptoCurrencyAccountContractData.java b/core/src/main/java/io/bitsquare/payment/CryptoCurrencyAccountContractData.java
index de5189da32a..a45cefccf84 100644
--- a/core/src/main/java/io/bitsquare/payment/CryptoCurrencyAccountContractData.java
+++ b/core/src/main/java/io/bitsquare/payment/CryptoCurrencyAccountContractData.java
@@ -42,7 +42,7 @@ public String getAddress() {
@Override
public String getPaymentDetails() {
- return "Receivers cryptocurrency address: " + address;
+ return "Receivers altcoin address: " + address;
}
@Override
diff --git a/core/src/main/java/io/bitsquare/payment/PaymentMethod.java b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java
index c22aea7591c..a72e8fd78e2 100644
--- a/core/src/main/java/io/bitsquare/payment/PaymentMethod.java
+++ b/core/src/main/java/io/bitsquare/payment/PaymentMethod.java
@@ -62,13 +62,13 @@ public final class PaymentMethod implements Persistable, Comparable {
public static final List ALL_VALUES = new ArrayList<>(Arrays.asList(
OK_PAY = new PaymentMethod(OK_PAY_ID, 0, DAY, Coin.parseCoin("1.5")), // tx instant so min. wait time
- SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY, Coin.parseCoin("0.75")), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
- NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 0, 4 * DAY, Coin.parseCoin("0.75")),
- SAME_BANK = new PaymentMethod(SAME_BANK_ID, 0, 2 * DAY, Coin.parseCoin("0.75")),
- SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 0, 4 * DAY, Coin.parseCoin("0.75")),
+ SEPA = new PaymentMethod(SEPA_ID, 0, 8 * DAY, Coin.parseCoin("1")), // sepa takes 1-3 business days. We use 8 days to include safety for holidays
+ NATIONAL_BANK = new PaymentMethod(NATIONAL_BANK_ID, 0, 4 * DAY, Coin.parseCoin("1")),
+ SAME_BANK = new PaymentMethod(SAME_BANK_ID, 0, 2 * DAY, Coin.parseCoin("1")),
+ SPECIFIC_BANKS = new PaymentMethod(SPECIFIC_BANKS_ID, 0, 4 * DAY, Coin.parseCoin("1")),
PERFECT_MONEY = new PaymentMethod(PERFECT_MONEY_ID, 0, DAY, Coin.parseCoin("1")),
- SWISH = new PaymentMethod(SWISH_ID, 0, DAY, Coin.parseCoin("1")),
- ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY, Coin.parseCoin("1")),
+ SWISH = new PaymentMethod(SWISH_ID, 0, DAY, Coin.parseCoin("1.5")),
+ ALI_PAY = new PaymentMethod(ALI_PAY_ID, 0, DAY, Coin.parseCoin("1.5")),
BLOCK_CHAINS = new PaymentMethod(BLOCK_CHAINS_ID, 0, DAY, Coin.parseCoin("2"))
));
diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java
index e1102eebe72..e78ffd01807 100644
--- a/core/src/main/java/io/bitsquare/trade/TradeManager.java
+++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java
@@ -66,7 +66,10 @@
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@@ -222,7 +225,7 @@ private void initPendingTrades() {
// We start later to have better connectivity to the network
UserThread.runAfter(() -> publishTradeStatistics(tradesForStatistics),
- 30, TimeUnit.SECONDS);
+ 90, TimeUnit.SECONDS);
pendingTradesInitialized.set(true);
}
@@ -238,10 +241,12 @@ private void publishTradeStatistics(List trades) {
keyRing.getPubKeyRing());
tradeStatisticsManager.add(tradeStatistics);
- // Only trades from last 30 days
- //TODO we want to get old trades published so we dont do the 30 days check for the first few weeks of the new version
- if (new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30)) || (new Date().getTime() - trade.getDate().getTime()) < TimeUnit.DAYS.toMillis(30)) {
- long delay = 3000;
+ // We only republish trades from last 10 days
+ // TODO check if needed at all. Don't want to remove it atm to not risk anything.
+ // But we could check which tradeStatistics we received from the seed nodes and
+ // only re-publish in case tradeStatistics are missing.
+ if ((new Date().getTime() - trade.getDate().getTime()) < TimeUnit.DAYS.toMillis(10)) {
+ long delay = 5000;
final long minDelay = (i + 1) * delay;
final long maxDelay = (i + 2) * delay;
UserThread.runAfterRandomDelay(() -> {
diff --git a/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java b/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java
index ee153f236ca..9fc08472bc3 100644
--- a/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java
+++ b/core/src/main/java/io/bitsquare/trade/closed/ClosedTradableManager.java
@@ -40,7 +40,10 @@ public class ClosedTradableManager {
@Inject
public ClosedTradableManager(KeyRing keyRing, PriceFeedService priceFeedService, @Named(Storage.DIR_KEY) File storageDir) {
this.keyRing = keyRing;
- this.closedTrades = new TradableList<>(new Storage<>(storageDir), "ClosedTrades");
+ final Storage> tradableListStorage = new Storage<>(storageDir);
+ // The ClosedTrades object can become a few MB so we don't keep so many backups
+ tradableListStorage.setNumMaxBackupFiles(3);
+ this.closedTrades = new TradableList<>(tradableListStorage, "ClosedTrades");
closedTrades.forEach(e -> e.getOffer().setPriceFeedService(priceFeedService));
}
diff --git a/core/src/main/java/io/bitsquare/trade/offer/FlatOffer.java b/core/src/main/java/io/bitsquare/trade/offer/FlatOffer.java
new file mode 100644
index 00000000000..dcd47ef983d
--- /dev/null
+++ b/core/src/main/java/io/bitsquare/trade/offer/FlatOffer.java
@@ -0,0 +1,46 @@
+package io.bitsquare.trade.offer;
+
+import io.bitsquare.payment.PaymentMethod;
+import org.bitcoinj.core.Coin;
+import org.bitcoinj.utils.Fiat;
+
+import java.util.Date;
+
+public class FlatOffer {
+ public final Offer.Direction direction;
+ public final String currencyCode;
+ public final long minAmount;
+ public final long amount;
+ public final long price;
+ public final long date;
+ public final boolean useMarketBasedPrice;
+ public final double marketPriceMargin;
+ public final String paymentMethod;
+ public final String id;
+ public final String offerFeeTxID;
+
+ public FlatOffer(Offer.Direction direction,
+ String currencyCode,
+ Coin minAmount,
+ Coin amount,
+ Fiat price,
+ Date date,
+ String id,
+ boolean useMarketBasedPrice,
+ double marketPriceMargin,
+ PaymentMethod paymentMethod,
+ String offerFeeTxID) {
+
+ this.direction = direction;
+ this.currencyCode = currencyCode;
+ this.minAmount = minAmount.value;
+ this.amount = amount.value;
+ this.price = price.value;
+ this.date = date.getTime();
+ this.id = id;
+ this.useMarketBasedPrice = useMarketBasedPrice;
+ this.marketPriceMargin = marketPriceMargin;
+ this.paymentMethod = paymentMethod.getId();
+ this.offerFeeTxID = offerFeeTxID;
+ }
+}
diff --git a/core/src/main/java/io/bitsquare/trade/offer/Offer.java b/core/src/main/java/io/bitsquare/trade/offer/Offer.java
index 0a7cafd2552..b992ca01e0c 100644
--- a/core/src/main/java/io/bitsquare/trade/offer/Offer.java
+++ b/core/src/main/java/io/bitsquare/trade/offer/Offer.java
@@ -54,6 +54,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload {
+
///////////////////////////////////////////////////////////////////////////////////////////
// Static
///////////////////////////////////////////////////////////////////////////////////////////
@@ -493,39 +494,44 @@ public ReadOnlyStringProperty errorMessageProperty() {
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Offer)) return false;
- Offer offer = (Offer) o;
- if (date != offer.date) return false;
- if (fiatPrice != offer.fiatPrice) return false;
- if (Double.compare(offer.marketPriceMargin, marketPriceMargin) != 0) return false;
- if (useMarketBasedPrice != offer.useMarketBasedPrice) return false;
- if (amount != offer.amount) return false;
- if (minAmount != offer.minAmount) return false;
- if (id != null ? !id.equals(offer.id) : offer.id != null) return false;
- if (direction != offer.direction) return false;
- if (currencyCode != null ? !currencyCode.equals(offer.currencyCode) : offer.currencyCode != null) return false;
- if (offererNodeAddress != null ? !offererNodeAddress.equals(offer.offererNodeAddress) : offer.offererNodeAddress != null)
+ Offer that = (Offer) o;
+ if (date != that.date) return false;
+ if (fiatPrice != that.fiatPrice) return false;
+ if (Double.compare(that.marketPriceMargin, marketPriceMargin) != 0) return false;
+ if (useMarketBasedPrice != that.useMarketBasedPrice) return false;
+ if (amount != that.amount) return false;
+ if (minAmount != that.minAmount) return false;
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ if (direction != null && that.direction != null && direction.ordinal() != that.direction.ordinal())
return false;
- if (pubKeyRing != null ? !pubKeyRing.equals(offer.pubKeyRing) : offer.pubKeyRing != null) return false;
- if (paymentMethodName != null ? !paymentMethodName.equals(offer.paymentMethodName) : offer.paymentMethodName != null)
+ else if ((direction == null && that.direction != null) || (direction != null && that.direction == null))
return false;
- if (countryCode != null ? !countryCode.equals(offer.countryCode) : offer.countryCode != null)
+
+ if (currencyCode != null ? !currencyCode.equals(that.currencyCode) : that.currencyCode != null) return false;
+ if (offererNodeAddress != null ? !offererNodeAddress.equals(that.offererNodeAddress) : that.offererNodeAddress != null)
+ return false;
+ if (pubKeyRing != null ? !pubKeyRing.equals(that.pubKeyRing) : that.pubKeyRing != null) return false;
+ if (paymentMethodName != null ? !paymentMethodName.equals(that.paymentMethodName) : that.paymentMethodName != null)
+ return false;
+ if (countryCode != null ? !countryCode.equals(that.countryCode) : that.countryCode != null)
return false;
- if (offererPaymentAccountId != null ? !offererPaymentAccountId.equals(offer.offererPaymentAccountId) : offer.offererPaymentAccountId != null)
+ if (offererPaymentAccountId != null ? !offererPaymentAccountId.equals(that.offererPaymentAccountId) : that.offererPaymentAccountId != null)
return false;
- if (acceptedCountryCodes != null ? !acceptedCountryCodes.equals(offer.acceptedCountryCodes) : offer.acceptedCountryCodes != null)
+ if (acceptedCountryCodes != null ? !acceptedCountryCodes.equals(that.acceptedCountryCodes) : that.acceptedCountryCodes != null)
return false;
- if (bankId != null ? !bankId.equals(offer.bankId) : offer.bankId != null) return false;
- if (acceptedBankIds != null ? !acceptedBankIds.equals(offer.acceptedBankIds) : offer.acceptedBankIds != null)
+ if (bankId != null ? !bankId.equals(that.bankId) : that.bankId != null) return false;
+ if (acceptedBankIds != null ? !acceptedBankIds.equals(that.acceptedBankIds) : that.acceptedBankIds != null)
return false;
- if (arbitratorNodeAddresses != null ? !arbitratorNodeAddresses.equals(offer.arbitratorNodeAddresses) : offer.arbitratorNodeAddresses != null)
+ if (arbitratorNodeAddresses != null ? !arbitratorNodeAddresses.equals(that.arbitratorNodeAddresses) : that.arbitratorNodeAddresses != null)
return false;
- return !(offerFeePaymentTxID != null ? !offerFeePaymentTxID.equals(offer.offerFeePaymentTxID) : offer.offerFeePaymentTxID != null);
+ return !(offerFeePaymentTxID != null ? !offerFeePaymentTxID.equals(that.offerFeePaymentTxID) : that.offerFeePaymentTxID != null);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
- result = 31 * result + (direction != null ? direction.hashCode() : 0);
+ result = 31 * result + (direction != null ? direction.ordinal() : 0);
result = 31 * result + (currencyCode != null ? currencyCode.hashCode() : 0);
result = 31 * result + (int) (date ^ (date >>> 32));
result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 32));
@@ -576,6 +582,7 @@ public String toString() {
"\n\terrorMessageProperty=" + errorMessageProperty +
"\n\tTAC_OFFERER=" + TAC_OFFERER +
"\n\tTAC_TAKER=" + TAC_TAKER +
+ "\n\thashCode=" + hashCode() +
'}';
}
}
diff --git a/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java b/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java
index 50a54afe948..b495cb53325 100644
--- a/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java
+++ b/core/src/main/java/io/bitsquare/trade/offer/OfferBookService.java
@@ -17,12 +17,19 @@
package io.bitsquare.trade.offer;
+import com.google.inject.name.Named;
+import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.btc.pricefeed.PriceFeedService;
+import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
+import io.bitsquare.common.util.Utilities;
+import io.bitsquare.p2p.BootstrapListener;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
+import io.bitsquare.storage.PlainTextWrapper;
+import io.bitsquare.storage.Storage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,6 +54,7 @@ public interface OfferBookChangedListener {
private final P2PService p2PService;
private PriceFeedService priceFeedService;
+ private final Storage offersJsonStorage;
private final List offerBookChangedListeners = new LinkedList<>();
@@ -55,9 +63,13 @@ public interface OfferBookChangedListener {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
- public OfferBookService(P2PService p2PService, PriceFeedService priceFeedService) {
+ public OfferBookService(P2PService p2PService,
+ PriceFeedService priceFeedService,
+ Storage offersJsonStorage,
+ @Named(CoreOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) {
this.p2PService = p2PService;
this.priceFeedService = priceFeedService;
+ this.offersJsonStorage = offersJsonStorage;
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
@@ -79,10 +91,28 @@ public void onRemoved(ProtectedStorageEntry data) {
});
}
});
- }
- public void addOfferBookChangedListener(OfferBookChangedListener offerBookChangedListener) {
- offerBookChangedListeners.add(offerBookChangedListener);
+ if (dumpStatistics) {
+ this.offersJsonStorage.initWithFileName("offers_statistics.json");
+
+ p2PService.addP2PServiceListener(new BootstrapListener() {
+ @Override
+ public void onBootstrapComplete() {
+ addOfferBookChangedListener(new OfferBookChangedListener() {
+ @Override
+ public void onAdded(Offer offer) {
+ doDumpStatistics();
+ }
+
+ @Override
+ public void onRemoved(Offer offer) {
+ doDumpStatistics();
+ }
+ });
+ UserThread.runAfter(OfferBookService.this::doDumpStatistics, 1);
+ }
+ });
+ }
}
@@ -140,4 +170,42 @@ public void removeOfferAtShutDown(Offer offer) {
public boolean isBootstrapped() {
return p2PService.isBootstrapped();
}
+
+ public void addOfferBookChangedListener(OfferBookChangedListener offerBookChangedListener) {
+ offerBookChangedListeners.add(offerBookChangedListener);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void doDumpStatistics() {
+ // We filter the case that it is a MarketBasedPrice but the price is not available
+ // That should only be possible if the price feed provider is not available
+ final List flatOffers = getOffers().stream()
+ .filter(offer -> !offer.getUseMarketBasedPrice() || priceFeedService.getMarketPrice(offer.getCurrencyCode()) != null)
+ .map(offer -> {
+ try {
+ return new FlatOffer(offer.getDirection(),
+ offer.getCurrencyCode(),
+ offer.getMinAmount(),
+ offer.getAmount(),
+ offer.getPrice(),
+ offer.getDate(),
+ offer.getId(),
+ offer.getUseMarketBasedPrice(),
+ offer.getMarketPriceMargin(),
+ offer.getPaymentMethod(),
+ offer.getOfferFeePaymentTxID()
+ );
+ } catch (Throwable t) {
+ // In case a offer was corrupted with null values we ignore it
+ return null;
+ }
+ })
+ .filter(e -> e != null)
+ .collect(Collectors.toList());
+ offersJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(flatOffers)), 5000);
+ }
}
diff --git a/core/src/main/java/io/bitsquare/trade/statistics/TradeStatistics.java b/core/src/main/java/io/bitsquare/trade/statistics/TradeStatistics.java
index ca3481c5901..e563dab863f 100644
--- a/core/src/main/java/io/bitsquare/trade/statistics/TradeStatistics.java
+++ b/core/src/main/java/io/bitsquare/trade/statistics/TradeStatistics.java
@@ -5,7 +5,8 @@
import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.util.JsonExclude;
import io.bitsquare.p2p.storage.payload.CapabilityRequiringPayload;
-import io.bitsquare.p2p.storage.payload.StoragePayload;
+import io.bitsquare.p2p.storage.payload.LazyProcessedStoragePayload;
+import io.bitsquare.p2p.storage.payload.PersistedStoragePayload;
import io.bitsquare.trade.offer.Offer;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.ExchangeRate;
@@ -19,7 +20,7 @@
import java.util.concurrent.TimeUnit;
@Immutable
-public final class TradeStatistics implements StoragePayload, CapabilityRequiringPayload {
+public final class TradeStatistics implements LazyProcessedStoragePayload, CapabilityRequiringPayload, PersistedStoragePayload {
@JsonExclude
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
@JsonExclude
@@ -110,12 +111,16 @@ public boolean equals(Object o) {
if (offerAmount != that.offerAmount) return false;
if (offerMinAmount != that.offerMinAmount) return false;
if (currency != null ? !currency.equals(that.currency) : that.currency != null) return false;
- if (direction != that.direction) return false;
+
+ if (direction != null && that.direction != null && direction.ordinal() != that.direction.ordinal())
+ return false;
+ else if ((direction == null && that.direction != null) || (direction != null && that.direction == null))
+ return false;
+
if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null)
return false;
if (offerId != null ? !offerId.equals(that.offerId) : that.offerId != null) return false;
return !(depositTxId != null ? !depositTxId.equals(that.depositTxId) : that.depositTxId != null);
-
}
@Override
@@ -123,7 +128,7 @@ public int hashCode() {
int result;
long temp;
result = currency != null ? currency.hashCode() : 0;
- result = 31 * result + (direction != null ? direction.hashCode() : 0);
+ result = 31 * result + (direction != null ? direction.ordinal() : 0);
result = 31 * result + (int) (tradePrice ^ (tradePrice >>> 32));
result = 31 * result + (int) (tradeAmount ^ (tradeAmount >>> 32));
result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0);
@@ -155,6 +160,7 @@ public String toString() {
", offerId='" + offerId + '\'' +
", depositTxId='" + depositTxId + '\'' +
", pubKeyRing=" + pubKeyRing +
+ ", hashCode=" + hashCode() +
'}';
}
}
diff --git a/core/src/main/java/io/bitsquare/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/io/bitsquare/trade/statistics/TradeStatisticsManager.java
index a9ddfb707a8..21ec2730570 100644
--- a/core/src/main/java/io/bitsquare/trade/statistics/TradeStatisticsManager.java
+++ b/core/src/main/java/io/bitsquare/trade/statistics/TradeStatisticsManager.java
@@ -10,6 +10,7 @@
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.payload.StoragePayload;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
+import io.bitsquare.storage.PlainTextWrapper;
import io.bitsquare.storage.Storage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
@@ -25,18 +26,18 @@ public class TradeStatisticsManager {
private static final Logger log = LoggerFactory.getLogger(TradeStatisticsManager.class);
private final Storage> statisticsStorage;
- private Storage fiatCurrencyListJsonStorage;
- private Storage cryptoCurrencyListJsonStorage;
- private Storage statisticsJsonStorage;
+ private Storage fiatCurrencyListJsonStorage;
+ private Storage cryptoCurrencyListJsonStorage;
+ private Storage statisticsJsonStorage;
private boolean dumpStatistics;
private ObservableSet observableTradeStatisticsSet = FXCollections.observableSet();
private HashSet tradeStatisticsSet = new HashSet<>();
@Inject
public TradeStatisticsManager(Storage> statisticsStorage,
- Storage fiatCurrencyListJsonStorage,
- Storage cryptoCurrencyListJsonStorage,
- Storage statisticsJsonStorage,
+ Storage fiatCurrencyListJsonStorage,
+ Storage cryptoCurrencyListJsonStorage,
+ Storage statisticsJsonStorage,
P2PService p2PService,
@Named(CoreOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) {
this.statisticsStorage = statisticsStorage;
@@ -45,20 +46,24 @@ public TradeStatisticsManager(Storage> statisticsStorag
this.statisticsJsonStorage = statisticsJsonStorage;
this.dumpStatistics = dumpStatistics;
+ init(p2PService);
+ }
+
+ private void init(P2PService p2PService) {
if (dumpStatistics) {
- this.statisticsJsonStorage.initAndGetPersistedWithFileName("trade_statistics.json");
+ this.statisticsJsonStorage.initWithFileName("trade_statistics.json");
- this.fiatCurrencyListJsonStorage.initAndGetPersistedWithFileName("fiat_currency_list.json");
+ this.fiatCurrencyListJsonStorage.initWithFileName("fiat_currency_list.json");
ArrayList fiatCurrencyList = new ArrayList<>(CurrencyUtil.getAllSortedFiatCurrencies().stream()
.map(e -> new CurrencyTuple(e.getCode(), e.getName()))
.collect(Collectors.toList()));
- fiatCurrencyListJsonStorage.queueUpForSave(Utilities.objectToJson(fiatCurrencyList), 2000);
+ fiatCurrencyListJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(fiatCurrencyList)), 2000);
- this.cryptoCurrencyListJsonStorage.initAndGetPersistedWithFileName("crypto_currency_list.json");
+ this.cryptoCurrencyListJsonStorage.initWithFileName("crypto_currency_list.json");
ArrayList cryptoCurrencyList = new ArrayList<>(CurrencyUtil.getAllSortedCryptoCurrencies().stream()
.map(e -> new CurrencyTuple(e.getCode(), e.getName()))
.collect(Collectors.toList()));
- cryptoCurrencyListJsonStorage.queueUpForSave(Utilities.objectToJson(cryptoCurrencyList), 2000);
+ cryptoCurrencyListJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(cryptoCurrencyList)), 2000);
}
HashSet persisted = statisticsStorage.initAndGetPersistedWithFileName("TradeStatistics");
@@ -78,6 +83,13 @@ public void onRemoved(ProtectedStorageEntry data) {
// We don't remove items
}
});
+
+ // At startup the P2PDataStorage inits earlier, otherwise we ge the listener called.
+ p2PService.getP2PDataStorage().getMap().values().forEach(e -> {
+ final StoragePayload storagePayload = e.getStoragePayload();
+ if (storagePayload instanceof TradeStatistics)
+ add((TradeStatistics) storagePayload);
+ });
}
public void add(TradeStatistics tradeStatistics) {
@@ -90,7 +102,7 @@ public void add(TradeStatistics tradeStatistics) {
dump();
} else {
- log.error("We have already an item with the same offer ID. That might happen if both the offerer and the taker published the tradeStatistics");
+ log.debug("We have already an item with the same offer ID. That might happen if both the offerer and the taker published the tradeStatistics");
}
}
}
@@ -111,8 +123,7 @@ private void dump() {
list.sort((o1, o2) -> (o1.tradeDate < o2.tradeDate ? 1 : (o1.tradeDate == o2.tradeDate ? 0 : -1)));
TradeStatistics[] array = new TradeStatistics[tradeStatisticsSet.size()];
list.toArray(array);
- statisticsJsonStorage.queueUpForSave(Utilities.objectToJson(array), 5000);
+ statisticsJsonStorage.queueUpForSave(new PlainTextWrapper(Utilities.objectToJson(array)), 5000);
}
}
-
}
diff --git a/doc/build.md b/doc/build.md
index 661ec87ebf8..4a79f134d2a 100644
--- a/doc/build.md
+++ b/doc/build.md
@@ -5,6 +5,8 @@ This guide will walk you through the process of building Bitsquare from source.
> _**NOTE:** For most users, building from source is not necessary. See the [releases page](https://github.com/bitsquare/bitsquare/releases), where you'll find installers for Windows, Linux and Mac OS X._
+There is an install script (2 parts) for setup (JDK, git, maven, Bitcoinj, Bitsquare) on Linux in that directory (install_on_unix.sh, install_on_unix_fin.sh).
+
System requirements
-------------
@@ -27,7 +29,7 @@ To install the Oracle JDK use:
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
- $ sudo apt-get -y oracle-java8-installer install
+ $ sudo apt-get -y install oracle-java8-installer
Check if $JAVA_HOME is set
@@ -71,11 +73,11 @@ You need to get the Bitsquare dependencies first as we need to copy the BountyCa
### 4. Copy the jdkfix jar file
-Copy the jdkfix-0.4.9.2.jar from the Bitsquare jdkfix/target directory to $JAVA_HOME/jre/lib/ext/.
-jdkfix-0.4.9.2.jar includes a bugfix of the SortedList class which will be released with the next JDK version.
+Copy the jdkfix-0.4.9.3.jar from the Bitsquare jdkfix/target directory to $JAVA_HOME/jre/lib/ext/.
+jdkfix-0.4.9.3.jar includes a bugfix of the SortedList class which will be released with the next JDK version.
We need to load that class before the default java class. This step will be removed once the bugfix is in the official JDK.
- $ sudo cp bitsquare/jdkfix/target/jdkfix-0.4.9.2.jar $JAVA_HOME/jre/lib/ext/jdkfix-0.4.9.2.jar
+ $ sudo cp bitsquare/jdkfix/target/jdkfix-0.4.9.3.jar $JAVA_HOME/jre/lib/ext/jdkfix-0.4.9.3.jar
### 5. Copy the BountyCastle provider jar file
@@ -105,6 +107,10 @@ You will get an error when building Bitsquare package if you don't have these.
$ unzip jce_policy-8.zip
$ sudo cp UnlimitedJCEPolicyJDK8/US_export_policy.jar $JAVA_HOME/jre/lib/security/US_export_policy.jar
$ sudo cp UnlimitedJCEPolicyJDK8/local_policy.jar $JAVA_HOME/jre/lib/security/local_policy.jar
+ $ sudo chmod 777 /usr/lib/jvm/java-8-oracle/jre/lib/security/US_export_policy.jar
+ $ sudo chmod 777 /usr/lib/jvm/java-8-oracle/jre/lib/security/local_policy.jar
+ $ sudo rm -r UnlimitedJCEPolicyJDK8
+ $ sudo rm jce_policy-8.zip
Build Bitsquare
-----------------
diff --git a/doc/install_on_unix.sh b/doc/install_on_unix.sh
new file mode 100755
index 00000000000..8000d8b92cb
--- /dev/null
+++ b/doc/install_on_unix.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+sudo -i
+
+JAVA_HOME=/usr/lib/jvm/java-8-oracle
+
+echo "Install Oracle Java 8, git, maven, unzip"
+apt-get update
+add-apt-repository ppa:webupd8team/java
+apt-get update
+apt-get -y install oracle-java8-installer git maven unzip
+
+
+echo "Enable unlimited Strength for cryptographic keys"
+wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jce/8/jce_policy-8.zip
+unzip jce_policy-8.zip
+cp UnlimitedJCEPolicyJDK8/US_export_policy.jar $JAVA_HOME/jre/lib/security/US_export_policy.jar
+cp UnlimitedJCEPolicyJDK8/local_policy.jar $JAVA_HOME/jre/lib/security/local_policy.jar
+
+chmod 777 $JAVA_HOME/jre/lib/security/US_export_policy.jar
+chmod 777 $JAVA_HOME/jre/lib/security/local_policy.jar
+
+rm -r UnlimitedJCEPolicyJDK8
+rm jce_policy-8.zip
+
+echo "Install bitcoinj"
+cd ~
+git clone -b FixBloomFilters https://github.com/bitsquare/bitcoinj.git
+cd bitcoinj
+mvn clean install -DskipTests -Dmaven.javadoc.skip=true
+
+echo "Install and resolve dependencies for bitsquare"
+cd ~
+git clone https://github.com/bitsquare/bitsquare.git
+cd bitsquare
+mvn clean package -DskipTests -Dmaven.javadoc.skip=true
+
+echo "Copy the jdkfix jar file"
+cp bitsquare/jdkfix/target/jdkfix-0.4.9.3.jar $JAVA_HOME/jre/lib/ext/jdkfix-0.4.9.3.jar
+
+echo "Add BountyCastle.jar"
+cd ~
+cp /root/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.53/bcprov-jdk15on-1.53.jar $JAVA_HOME/jre/lib/ext/bcprov-jdk15on-1.53.jar
+
+
diff --git a/doc/install_on_unix_fin.sh b/doc/install_on_unix_fin.sh
new file mode 100755
index 00000000000..2903364a65f
--- /dev/null
+++ b/doc/install_on_unix_fin.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+sudo -i
+
+JAVA_HOME=/usr/lib/jvm/java-8-oracle
+
+# Before running the second part edit $JAVA_HOME/jre/lib/security/java.security file
+# add line: security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
+
+# and add JAVA_HOME to .bashrc
+# export JAVA_HOME=/usr/lib/jvm/java-8-oracle
+
+echo "Install bitsquare"
+cd ~/bitsquare
+mvn clean package -DskipTests -Dmaven.javadoc.skip=true
+cd ..
+mkdir .local
+mkdir .local/share
+
+echo "Start bitsquare"
+java -jar ~/bitsquare/gui/target/shaded.jar
+
+
diff --git a/gui/pom.xml b/gui/pom.xml
index 0fe6f1ab14e..2debf3f952c 100644
--- a/gui/pom.xml
+++ b/gui/pom.xml
@@ -22,7 +22,7 @@
parent
io.bitsquare
- 0.4.9.2
+ 0.4.9.3
4.0.0
diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java
index 1f4531bccf0..0f585be533c 100644
--- a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java
+++ b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java
@@ -27,6 +27,7 @@
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ResultHandler;
+import io.bitsquare.common.util.Profiler;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.filter.FilterManager;
import io.bitsquare.gui.SystemTray;
@@ -83,6 +84,8 @@
public class BitsquareApp extends Application {
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
+ private static final long LOG_MEMORY_PERIOD_MIN = 10;
+
private static Environment env;
private BitsquareAppModule bitsquareAppModule;
@@ -237,6 +240,8 @@ else if (Utilities.isWindows())
.show();
}
+ UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES);
+
} catch (Throwable throwable) {
showErrorPopup(throwable, false);
}
diff --git a/gui/src/main/java/io/bitsquare/gui/Navigation.java b/gui/src/main/java/io/bitsquare/gui/Navigation.java
index a5bac398d97..28ed5c6a0af 100644
--- a/gui/src/main/java/io/bitsquare/gui/Navigation.java
+++ b/gui/src/main/java/io/bitsquare/gui/Navigation.java
@@ -59,6 +59,7 @@ public interface Listener {
@Inject
public Navigation(Storage storage) {
this.storage = storage;
+ storage.setNumMaxBackupFiles(3);
Navigation persisted = storage.initAndGetPersisted(this);
if (persisted != null) {
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java
index f64e56cdc47..ce1e98dafec 100644
--- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/AliPayForm.java
@@ -73,9 +73,9 @@ public void addFormForAddAccount() {
protected void autoFillNameTextField() {
if (useCustomAccountNameCheckBox != null && !useCustomAccountNameCheckBox.isSelected()) {
String accountNr = accountNrInputTextField.getText();
- accountNr = StringUtils.abbreviate(accountNr, 5);
+ accountNr = StringUtils.abbreviate(accountNr, 9);
String method = BSResources.get(paymentAccount.getPaymentMethod().getId());
- accountNameTextField.setText(method.concat(", ").concat(accountNr));
+ accountNameTextField.setText(method.concat(": ").concat(accountNr));
}
}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java
index 6f6dd5a0dd3..dce3aaa6a43 100644
--- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/BankForm.java
@@ -470,27 +470,27 @@ protected void autoFillNameTextField() {
countryCode = "";
if (BankUtil.isBankIdRequired(countryCode)) {
bankId = bankIdInputTextField.getText();
- if (bankId.length() > 6)
+ if (bankId.length() > 9)
bankId = StringUtils.abbreviate(bankId, 9);
} else if (BankUtil.isBranchIdRequired(countryCode)) {
bankId = branchIdInputTextField.getText();
- if (bankId.length() > 6)
+ if (bankId.length() > 9)
bankId = StringUtils.abbreviate(bankId, 9);
} else if (BankUtil.isBankNameRequired(countryCode)) {
bankId = bankNameInputTextField.getText();
- if (bankId.length() > 6)
+ if (bankId.length() > 9)
bankId = StringUtils.abbreviate(bankId, 9);
}
String accountNr = accountNrInputTextField.getText();
- if (accountNr.length() > 6)
+ if (accountNr.length() > 9)
accountNr = StringUtils.abbreviate(accountNr, 9);
String method = BSResources.get(paymentAccount.getPaymentMethod().getId());
- if (bankId != null)
- accountNameTextField.setText(method.concat(", ").concat(bankId).concat(", ").concat(accountNr));
+ if (bankId != null && !bankId.isEmpty())
+ accountNameTextField.setText(method.concat(": ").concat(bankId).concat(", ").concat(accountNr));
else
- accountNameTextField.setText(method.concat(", ").concat(accountNr));
+ accountNameTextField.setText(method.concat(": ").concat(accountNr));
}
}
diff --git a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/CryptoCurrencyForm.java b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/CryptoCurrencyForm.java
index 476e3b80732..1fc5a3a8014 100644
--- a/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/CryptoCurrencyForm.java
+++ b/gui/src/main/java/io/bitsquare/gui/components/paymentmethods/CryptoCurrencyForm.java
@@ -72,7 +72,7 @@ public void addFormForAddAccount() {
addTradeCurrencyComboBox();
currencyComboBox.setPrefWidth(250);
- Tuple2