Merge branch 'Development'

This commit is contained in:
Manfred Karrer 2017-02-11 19:24:46 -05:00
commit 20571e03df
131 changed files with 2574 additions and 385 deletions

2
.gitignore vendored
View file

@ -26,3 +26,5 @@ build
desktop.ini
*.bat
!package/windows/*bit*.bat
*/target/*
*.class

View file

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>io.bitsquare</groupId>
<version>0.4.9.8</version>
<version>0.4.9.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View file

@ -24,6 +24,7 @@ import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.util.FileSize;
import io.bitsquare.common.util.Profiler;
import org.slf4j.LoggerFactory;
@ -54,7 +55,7 @@ public class Log {
rollingPolicy.start();
triggeringPolicy = new SizeBasedTriggeringPolicy();
triggeringPolicy.setMaxFileSize("10MB");
triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB"));
triggeringPolicy.start();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();

View file

@ -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.8";
public static final String VERSION = "0.4.9.9";
// The version no. 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.
@ -43,7 +43,7 @@ public class Version {
// The version no. of the current protocol. The offer holds that version.
// A taker will check the version of the offers to see if his version is compatible.
public static final int TRADE_PROTOCOL_VERSION = 1;
public static final int TRADE_PROTOCOL_VERSION = 2;
private static int p2pMessageVersion;
public static int getP2PMessageVersion() {

View file

@ -26,6 +26,7 @@ import com.google.gson.*;
import io.bitsquare.io.LookAheadObjectInputStream;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -420,4 +421,45 @@ public class Utilities {
public static String toTruncatedString(Object message) {
return toTruncatedString(message, 200);
}
public static String getRandomPrefix(int minLength, int maxLength) {
int length = minLength + new Random().nextInt(maxLength - minLength + 1);
String result;
switch (new Random().nextInt(3)) {
case 0:
result = RandomStringUtils.randomAlphabetic(length);
break;
case 1:
result = RandomStringUtils.randomNumeric(length);
break;
case 2:
default:
result = RandomStringUtils.randomAlphanumeric(length);
}
switch (new Random().nextInt(3)) {
case 0:
result = result.toUpperCase();
break;
case 1:
result = result.toLowerCase();
break;
case 2:
default:
}
return result;
}
public static String getShortId(String id) {
return getShortId(id, "-");
}
public static String getShortId(String id, String sep) {
String[] chunks = id.split(sep);
if (chunks.length > 0)
return chunks[0];
else
return id.substring(0, Math.min(8, id.length()));
}
}

View file

@ -6,34 +6,11 @@
<parent>
<artifactId>parent</artifactId>
<groupId>io.bitsquare</groupId>
<version>0.4.9.8</version>
<version>0.4.9.9</version>
</parent>
<artifactId>core</artifactId>
<build>
<plugins>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<DependencyConvergence />
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>-->
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.bitsquare</groupId>

View file

@ -17,6 +17,7 @@
package io.bitsquare.alert;
import com.google.common.annotations.VisibleForTesting;
import io.bitsquare.app.Version;
import io.bitsquare.common.crypto.Sig;
import io.bitsquare.p2p.storage.payload.StoragePayload;
@ -69,9 +70,14 @@ public final class Alert implements StoragePayload {
}
public boolean isNewVersion() {
return isNewVersion(Version.VERSION);
}
@VisibleForTesting
protected boolean isNewVersion(String appVersion) {
// Usually we use 3 digits (0.4.8) but to support also 4 digits in case of hotfixes (0.4.8.1) we
// add a 0 at all 3 digit versions to allow correct comparison: 0.4.8 -> 480; 0.4.8.1 -> 481; 481 > 480
String myVersionString = Version.VERSION.replace(".", "");
String myVersionString = appVersion.replace(".", "");
if (myVersionString.length() == 3)
myVersionString += "0";
int versionNum = Integer.valueOf(myVersionString);

View file

@ -62,7 +62,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
public static final String DEFAULT_USER_DATA_DIR = defaultUserDataDir();
public static final String DEFAULT_APP_DATA_DIR = appDataDir(DEFAULT_USER_DATA_DIR, DEFAULT_APP_NAME);
public static final String LOG_LEVEL_DEFAULT = (DevFlags.STRESS_TEST_MODE || DevFlags.DEV_MODE) ? Level.TRACE.levelStr : Level.INFO.levelStr;
public static final String LOG_LEVEL_DEFAULT = Level.INFO.levelStr;
static final String BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME = "bitsquareCommandLineProperties";
static final String BITSQUARE_APP_DIR_PROPERTY_SOURCE_NAME = "bitsquareAppDirProperties";

View file

@ -93,6 +93,9 @@ public abstract class BitsquareExecutable {
.withRequiredArg();
parser.accepts(NetworkOptionKeys.SOCKS_5_PROXY_HTTP_ADDRESS, description("A proxy address to be used for Http requests (should be non-Tor). [host:port]", ""))
.withRequiredArg();
parser.accepts(NetworkOptionKeys.SOCKS5_DISCOVER_MODE, description("Specify discovery mode for Bitcoin nodes. One or more of: [ADDR, DNS, ONION, ALL]" +
" (comma separated, they get OR'd together). Default value is ALL", "ALL"))
.withRequiredArg();
parser.accepts(AppOptionKeys.USER_DATA_DIR_KEY, description("User data directory", DEFAULT_USER_DATA_DIR))
.withRequiredArg();

View file

@ -83,8 +83,7 @@ public class ArbitratorManager {
"02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b",
"03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169",
"0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72",
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5",
"0274f772a98d23e7a0251ab30d7121897b5aebd11a2f1e45ab654aa57503173245"
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5"
));

View file

@ -20,6 +20,7 @@ package io.bitsquare.arbitration;
import io.bitsquare.app.Version;
import io.bitsquare.arbitration.messages.DisputeCommunicationMessage;
import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.common.wire.Payload;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Contract;
@ -195,7 +196,7 @@ public final class Dispute implements Payload {
}
public String getShortTradeId() {
return tradeId.substring(0, 8);
return Utilities.getShortId(tradeId);
}
public int getTraderId() {

View file

@ -19,6 +19,7 @@ package io.bitsquare.btc;
import io.bitsquare.app.Version;
import io.bitsquare.common.persistance.Persistable;
import io.bitsquare.common.util.Utilities;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters;
@ -136,7 +137,7 @@ public final class AddressEntry implements Persistable {
// For display we usually only display the first 8 characters.
@Nullable
public String getShortOfferId() {
return getOfferId() != null ? getOfferId().substring(0, Math.min(8, getOfferId().length())) : null;
return offerId != null ? Utilities.getShortId(offerId) : null;
}
public Context getContext() {

View file

@ -25,26 +25,27 @@ public class FeePolicy {
// With block getting filled up the needed fee to get fast into a black has become more expensive and less predictable.
// To see current fees check out:
// https://tradeblock.com/blockchain
// http://www.cointape.com
// https://jochen-hoenicke.de/queue/24h.html
// https://bitcoinfees.21.co/
// http://p2sh.info/dashboard/db/fee-estimation
// https://bitcoinfees.github.io/#1d
// Average values are 10-100 satoshis/byte in january 2016
// Average values are 60-140 satoshis/byte in february 2017
//
// Our trade transactions have a fixed set of inputs and outputs making the size very predictable
// (as long the user does not do multiple funding transactions)
//
// trade fee tx: 226 bytes // 88 satoshi/byte
// deposit tx: 336 bytes // 59 satoshi/byte
// payout tx: 371 bytes // 53 satoshi/byte
// disputed payout tx: 408 bytes // 49 satoshi/byte
// trade fee tx: 226 bytes // 221 satoshi/byte
// deposit tx: 336 bytes // 148 satoshi/byte
// payout tx: 371 bytes // 134 satoshi/byte
// disputed payout tx: 408 bytes // 122 satoshi/byte
// We set a fixed fee to make the needed amounts in the trade predictable.
// We use 0.0002 BTC (0.08 EUR @ 400 EUR/BTC) which is for our tx sizes about 50-90 satoshi/byte
// We use 0.0005 BTC (0.5 EUR @ 1000 EUR/BTC) which is for our tx sizes about 120-220 satoshi/byte
// We cannot make that user defined as it need to be the same for both users, so we can only change that in
// software updates
// TODO before Beta we should get a good future proof guess as a change causes incompatible versions
public static Coin getFixedTxFeeForTrades() {
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(20_000);
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(50_000);
}
// For non trade transactions (withdrawal) we use the default fee calculation
@ -53,7 +54,7 @@ public class FeePolicy {
// The BitcoinJ fee calculation use kb so a tx size < 1kb will still pay the fee for a kb tx.
// Our payout tx has about 370 bytes so we get a fee/kb value of about 90 satoshi/byte making it high priority
// Other payout transactions (E.g. arbitrators many collected transactions) will go with 30 satoshi/byte if > 1kb
private static Coin NON_TRADE_FEE_PER_KB = DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(20_000); // 0.0002 BTC about 0.08 EUR @ 400 EUR/BTC
private static Coin NON_TRADE_FEE_PER_KB = DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(30_000); // 0.0003 BTC about 0.3 EUR @ 1000 EUR/BTC
public static void setNonTradeFeePerKb(Coin nonTradeFeePerKb) {
NON_TRADE_FEE_PER_KB = nonTradeFeePerKb;
@ -63,22 +64,20 @@ public class FeePolicy {
return NON_TRADE_FEE_PER_KB;
}
// 0.0005 BTC 0.05% of 1 BTC about 0.2 EUR @ 400 EUR/BTC
// 0.0008 BTC 0.08% of 1 BTC about 0.8 EUR @ 1000 EUR/BTC
public static Coin getCreateOfferFee() {
// We need to pay the quite high miner fee of 30_000 from the trading fee tx so 30_000 us our lower limit
// The arbitrator receive only 0.0002 BTC - less than the miners
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(10_000) : Coin.valueOf(50_000);
// We need to pay the quite high miner fee of 50_000 from the trading fee tx so 30_000 is what
// the arbitrator receives
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(10_000) : Coin.valueOf(80_000);
}
// 0.001 BTC 0.1% of 1 BTC about 0.4 EUR @ 400 EUR/BTC
// 0.001 BTC 0.1% of 1 BTC about 1 EUR @ 1000 EUR/BTC
public static Coin getTakeOfferFee() {
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(10_000) : Coin.valueOf(100_000);
}
// TODO will be increased once we get higher limits
// 0.01 BTC; about 4 EUR @ 400 EUR/BTC
// 0.03 BTC; about 30 EUR @ 1000 EUR/BTC
public static Coin getSecurityDeposit() {
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(1_000_000);
return DevFlags.STRESS_TEST_MODE ? Coin.valueOf(5_000) : Coin.valueOf(3_000_000);
}
}

View file

@ -238,7 +238,7 @@ public class TradeWalletService {
verifyTransaction(dummyTX);
printTxWithInputs("dummyTX", dummyTX);
//printTxWithInputs("dummyTX", dummyTX);
List<RawTransactionInput> rawTransactionInputList = dummyTX.getInputs().stream()
.map(e -> {
@ -407,7 +407,7 @@ public class TradeWalletService {
verifyTransaction(preparedDepositTx);
printTxWithInputs("preparedDepositTx", preparedDepositTx);
//printTxWithInputs("preparedDepositTx", preparedDepositTx);
return new PreparedDepositTxAndOffererInputs(offererRawTransactionInputs, preparedDepositTx.bitcoinSerialize());
}
@ -428,15 +428,15 @@ public class TradeWalletService {
* @throws TransactionVerificationException
* @throws WalletException
*/
public void takerSignsAndPublishesDepositTx(boolean takerIsSeller,
byte[] contractHash,
byte[] offerersDepositTxSerialized,
List<RawTransactionInput> buyerInputs,
List<RawTransactionInput> sellerInputs,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey,
FutureCallback<Transaction> callback) throws SigningException, TransactionVerificationException,
public Transaction takerSignsAndPublishesDepositTx(boolean takerIsSeller,
byte[] contractHash,
byte[] offerersDepositTxSerialized,
List<RawTransactionInput> buyerInputs,
List<RawTransactionInput> sellerInputs,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey,
FutureCallback<Transaction> callback) throws SigningException, TransactionVerificationException,
WalletException {
Transaction offerersDepositTx = new Transaction(params, offerersDepositTxSerialized);
@ -493,7 +493,7 @@ public class TradeWalletService {
// Add all outputs from offerersDepositTx to depositTx
offerersDepositTx.getOutputs().forEach(depositTx::addOutput);
printTxWithInputs("offerersDepositTx", offerersDepositTx);
//printTxWithInputs("offerersDepositTx", offerersDepositTx);
// Sign inputs
int start = takerIsSeller ? buyerInputs.size() : 0;
@ -513,6 +513,8 @@ public class TradeWalletService {
checkNotNull(walletAppKit);
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(depositTx).future();
Futures.addCallback(broadcastComplete, callback);
return depositTx;
}
@ -575,7 +577,7 @@ public class TradeWalletService {
verifyTransaction(preparedPayoutTx);
printTxWithInputs("preparedPayoutTx", preparedPayoutTx);
//printTxWithInputs("preparedPayoutTx", preparedPayoutTx);
return sellerSignature.encodeToDER();
}
@ -683,16 +685,16 @@ public class TradeWalletService {
* @throws AddressFormatException
* @throws TransactionVerificationException
*/
public byte[] signDisputedPayoutTx(byte[] depositTxSerialized,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
Coin arbitratorPayoutAmount,
String buyerAddressString,
String sellerAddressString,
AddressEntry arbitratorAddressEntry,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
public byte[] arbitratorSignsDisputedPayoutTx(byte[] depositTxSerialized,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
Coin arbitratorPayoutAmount,
String buyerAddressString,
String sellerAddressString,
AddressEntry arbitratorAddressEntry,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
throws AddressFormatException, TransactionVerificationException {
Transaction depositTx = new Transaction(params, depositTxSerialized);
log.trace("signDisputedPayoutTx called");
@ -730,7 +732,7 @@ public class TradeWalletService {
verifyTransaction(preparedPayoutTx);
printTxWithInputs("preparedPayoutTx", preparedPayoutTx);
//printTxWithInputs("preparedPayoutTx", preparedPayoutTx);
return arbitratorSignature.encodeToDER();
}
@ -827,6 +829,136 @@ public class TradeWalletService {
}
// Emergency payout tool. Used only in cased when the payput from the arbitrator does not work because some data
// in the trade/dispute are messed up.
public Transaction emergencySignAndPublishPayoutTx(String depositTxHex,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
Coin arbitratorPayoutAmount,
String buyerAddressString,
String sellerAddressString,
String arbitratorAddressString,
@Nullable String buyerPrivateKeyAsHex,
@Nullable String sellerPrivateKeyAsHex,
String arbitratorPrivateKeyAsHex,
String buyerPubKeyAsHex,
String sellerPubKeyAsHex,
String arbitratorPubKeyAsHex,
String P2SHMultiSigOutputScript,
List<String> buyerPubKeys,
List<String> sellerPubKeys,
FutureCallback<Transaction> callback)
throws AddressFormatException, TransactionVerificationException, WalletException {
log.info("signAndPublishPayoutTx called");
log.info("depositTxHex " + depositTxHex);
log.info("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString());
log.info("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString());
log.info("arbitratorPayoutAmount " + arbitratorPayoutAmount.toFriendlyString());
log.info("buyerAddressString " + buyerAddressString);
log.info("sellerAddressString " + sellerAddressString);
log.info("arbitratorAddressString " + arbitratorAddressString);
log.info("buyerPrivateKeyAsHex (not displayed for security reasons)");
log.info("sellerPrivateKeyAsHex (not displayed for security reasons)");
log.info("arbitratorPrivateKeyAsHex (not displayed for security reasons)");
log.info("buyerPubKeyAsHex " + buyerPubKeyAsHex);
log.info("sellerPubKeyAsHex " + sellerPubKeyAsHex);
log.info("arbitratorPubKeyAsHex " + arbitratorPubKeyAsHex);
log.info("P2SHMultiSigOutputScript " + P2SHMultiSigOutputScript);
log.info("buyerPubKeys " + buyerPubKeys);
log.info("sellerPubKeys " + sellerPubKeys);
checkNotNull((buyerPrivateKeyAsHex != null || sellerPrivateKeyAsHex != null), "either buyerPrivateKeyAsHex or sellerPrivateKeyAsHex must not be null");
byte[] buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(buyerPubKeyAsHex)).getPubKey();
byte[] sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(sellerPubKeyAsHex)).getPubKey();
final byte[] arbitratorPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(arbitratorPubKeyAsHex)).getPubKey();
Script p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
if (!p2SHMultiSigOutputScript.toString().contains(P2SHMultiSigOutputScript)) {
if (buyerPubKeys.isEmpty())
buyerPubKeys.add(buyerPubKeyAsHex);
if (sellerPubKeys.isEmpty())
sellerPubKeys.add(sellerPubKeyAsHex);
boolean found = false;
for (String b : buyerPubKeys) {
if (found)
break;
byte[] bk = ECKey.fromPublicOnly(Utils.HEX.decode(b)).getPubKey();
for (String s : sellerPubKeys) {
byte[] sk = ECKey.fromPublicOnly(Utils.HEX.decode(s)).getPubKey();
p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(bk, sk, arbitratorPubKey);
if (p2SHMultiSigOutputScript.toString().contains(P2SHMultiSigOutputScript)) {
log.info("Found buyers pub key " + b);
log.info("Found sellers pub key " + s);
buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(b)).getPubKey();
sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(s)).getPubKey();
found = true;
break;
}
}
}
if (!found)
log.warn("We did not find any matching pub keys for generating the required p2SHMultiSigOutputScript");
}
Coin msOutput = buyerPayoutAmount.add(sellerPayoutAmount).add(arbitratorPayoutAmount).add(FeePolicy.getFixedTxFeeForTrades());
TransactionOutput p2SHMultiSigOutput = new TransactionOutput(params, null, msOutput, p2SHMultiSigOutputScript.getProgram());
Transaction depositTx = new Transaction(params);
depositTx.addOutput(p2SHMultiSigOutput);
Transaction payoutTx = new Transaction(params);
Sha256Hash spendTxHash = Sha256Hash.wrap(depositTxHex);
payoutTx.addInput(new TransactionInput(params, depositTx, p2SHMultiSigOutputScript.getProgram(), new TransactionOutPoint(params, 0, spendTxHash), msOutput));
if (buyerPayoutAmount.isGreaterThan(Coin.ZERO))
payoutTx.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString));
if (sellerPayoutAmount.isGreaterThan(Coin.ZERO))
payoutTx.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString));
if (arbitratorPayoutAmount.isGreaterThan(Coin.ZERO))
payoutTx.addOutput(arbitratorPayoutAmount, new Address(params, arbitratorAddressString));
// take care of sorting!
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature tradersSignature;
if (buyerPrivateKeyAsHex != null && !buyerPrivateKeyAsHex.isEmpty()) {
final ECKey buyerPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(buyerPrivateKeyAsHex));
checkNotNull(buyerPrivateKey, "buyerPrivateKey must not be null");
tradersSignature = buyerPrivateKey.sign(sigHash, aesKey).toCanonicalised();
} else {
checkNotNull(sellerPrivateKeyAsHex, "sellerPrivateKeyAsHex must not be null");
final ECKey sellerPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(sellerPrivateKeyAsHex));
checkNotNull(sellerPrivateKey, "sellerPrivateKey must not be null");
tradersSignature = sellerPrivateKey.sign(sigHash, aesKey).toCanonicalised();
}
final ECKey key = ECKey.fromPrivate(Utils.HEX.decode(arbitratorPrivateKeyAsHex));
checkNotNull(key, "key must not be null");
ECKey.ECDSASignature arbitratorSignature = key.sign(sigHash, aesKey).toCanonicalised();
TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false);
TransactionSignature arbitratorTxSig = new TransactionSignature(arbitratorSignature, Transaction.SigHash.ALL, false);
// Take care of order of signatures. See comment below at getMultiSigRedeemScript (sort order needed here: arbitrator, seller, buyer)
Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(arbitratorTxSig, tradersTxSig), redeemScript);
TransactionInput input = payoutTx.getInput(0);
input.setScriptSig(inputScript);
printTxWithInputs("payoutTx", payoutTx);
verifyTransaction(payoutTx);
checkWalletConsistency();
if (walletAppKit != null) {
ListenableFuture<Transaction> future = walletAppKit.peerGroup().broadcastTransaction(payoutTx).future();
Futures.addCallback(future, callback);
}
return payoutTx;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////////////////
@ -990,12 +1122,16 @@ public class TradeWalletService {
}
private static void printTxWithInputs(String tracePrefix, Transaction tx) {
log.trace(tracePrefix + ": " + tx.toString());
long fee = tx.getFee() != null ? tx.getFee().value : 0;
int size = tx.getMessageSize();
log.info(tracePrefix + ": " + tx.toString() + "\nSize (Byte): " + size + "\nFee (Satoshi/Byte): "
+ (fee / size));
for (TransactionInput input : tx.getInputs()) {
if (input.getConnectedOutput() != null)
log.trace(tracePrefix + " input value: " + input.getConnectedOutput().getValue().toFriendlyString());
log.info(tracePrefix + " input value: " + input.getConnectedOutput().getValue().toFriendlyString());
else
log.trace(tracePrefix + ": Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
log.info(tracePrefix + ": Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
}
}

View file

@ -17,6 +17,7 @@
package io.bitsquare.btc;
import com.google.common.net.InetAddresses;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.PeerGroup;
@ -24,8 +25,10 @@ import org.bitcoinj.kits.WalletAppKit;
import org.bitcoinj.net.BlockingClientManager;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.util.concurrent.TimeoutException;
public class WalletAppKitBitSquare extends WalletAppKit {
@ -44,25 +47,41 @@ public class WalletAppKitBitSquare extends WalletAppKit {
}
protected PeerGroup createPeerGroup() throws TimeoutException {
// no proxy case.
if (socks5Proxy == null) {
if (socks5Proxy == null || isLocalHostFullNodeRunning()) {
return super.createPeerGroup();
} else {
// proxy case.
Proxy proxy = new Proxy(Proxy.Type.SOCKS,
new InetSocketAddress(socks5Proxy.getInetAddress().getHostName(),
socks5Proxy.getPort()));
int CONNECT_TIMEOUT_MSEC = 60 * 1000; // same value used in bitcoinj.
ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy);
BlockingClientManager mgr = new BlockingClientManager(proxySocketFactory);
PeerGroup peerGroup = new PeerGroup(params, vChain, mgr);
mgr.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
peerGroup.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
return peerGroup;
}
}
// proxy case.
Proxy proxy = new Proxy(Proxy.Type.SOCKS,
new InetSocketAddress(socks5Proxy.getInetAddress().getHostName(),
socks5Proxy.getPort()));
int CONNECT_TIMEOUT_MSEC = 60 * 1000; // same value used in bitcoinj.
ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy);
BlockingClientManager mgr = new BlockingClientManager(proxySocketFactory);
PeerGroup peerGroup = new PeerGroup(params, vChain, mgr);
mgr.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
peerGroup.setConnectTimeoutMillis(CONNECT_TIMEOUT_MSEC);
return peerGroup;
private boolean isLocalHostFullNodeRunning() {
// We check first if a local node is running, if so we connect direct without proxy.
// Borrowed form PeerGroup.maybeCheckForLocalhostPeer()
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), params.getPort()), PeerGroup.DEFAULT_CONNECT_TIMEOUT_MILLIS);
try {
socket.close();
} catch (IOException ignore) {
}
return true;
} catch (IOException e) {
log.debug("Localhost peer not detected.");
return false;
}
}
}

View file

@ -32,15 +32,18 @@ import io.bitsquare.common.UserThread;
import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ExceptionHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.network.DnsLookupTor;
import io.bitsquare.network.NetworkOptionKeys;
import io.bitsquare.network.Socks5MultiDiscovery;
import io.bitsquare.network.Socks5ProxyProvider;
import io.bitsquare.storage.FileUtil;
import io.bitsquare.storage.Storage;
import io.bitsquare.user.Preferences;
import javafx.beans.property.*;
import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.core.*;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.net.discovery.SeedPeers;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
@ -92,6 +95,7 @@ public class WalletService {
private final NetworkParameters params;
private final File walletDir;
private final UserAgent userAgent;
private final int socks5DiscoverMode;
private WalletAppKitBitSquare walletAppKit;
private Wallet wallet;
@ -114,7 +118,8 @@ public class WalletService {
UserAgent userAgent,
Preferences preferences,
Socks5ProxyProvider socks5ProxyProvider,
@Named(BtcOptionKeys.WALLET_DIR) File appDir) {
@Named(BtcOptionKeys.WALLET_DIR) File appDir,
@Named(NetworkOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) {
this.regTestHost = regTestHost;
this.tradeWalletService = tradeWalletService;
this.addressEntryList = addressEntryList;
@ -132,6 +137,27 @@ public class WalletService {
bloomFilterTweak = new Random().nextLong();
storage.queueUpForSave(bloomFilterTweak, 100);
}
String[] socks5DiscoverModes = StringUtils.deleteWhitespace(socks5DiscoverModeString).split(",");
int mode = 0;
for (int i = 0; i < socks5DiscoverModes.length; i++) {
switch (socks5DiscoverModes[i]) {
case "ADDR":
mode |= Socks5MultiDiscovery.SOCKS5_DISCOVER_ADDR;
break;
case "DNS":
mode |= Socks5MultiDiscovery.SOCKS5_DISCOVER_DNS;
break;
case "ONION":
mode |= Socks5MultiDiscovery.SOCKS5_DISCOVER_ONION;
break;
case "ALL":
default:
mode |= Socks5MultiDiscovery.SOCKS5_DISCOVER_ALL;
break;
}
}
socks5DiscoverMode = mode;
}
@ -172,6 +198,7 @@ public class WalletService {
addressEntryList.onWalletReady(wallet);
walletAppKit.peerGroup().addEventListener(new PeerEventListener() {
@Override
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
@ -265,9 +292,7 @@ public class WalletService {
// Pass custom seed nodes if set in options
if (!btcNodes.isEmpty()) {
// TODO: this parsing should be more robust,
// give validation error if needed.
String[] nodes = btcNodes.replace(", ", ",").split(",");
String[] nodes = StringUtils.deleteWhitespace(btcNodes).split(",");
List<PeerAddress> peerAddressList = new ArrayList<>();
for (String node : nodes) {
String[] parts = node.split(":");
@ -278,19 +303,23 @@ public class WalletService {
if (parts.length == 2) {
// note: this will cause a DNS request if hostname used.
// note: DNS requests are routed over socks5 proxy, if used.
// fixme: .onion hostnames will fail! see comments in SeedPeersSocks5Dns
// note: .onion hostnames will be unresolved.
InetSocketAddress addr;
if (socks5Proxy != null) {
InetSocketAddress unresolved = InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1]));
// proxy remote DNS request happens here.
addr = SeedPeersSocks5Dns.lookup(socks5Proxy, unresolved);
try {
// proxy remote DNS request happens here. blocking.
addr = new InetSocketAddress(DnsLookupTor.lookup(socks5Proxy, parts[0]), Integer.parseInt(parts[1]));
} catch (Exception e) {
log.warn("Dns lookup failed for host: {}", parts[0]);
addr = null;
}
} else {
// DNS request happens here. if it fails, addr.isUnresolved() == true.
addr = new InetSocketAddress(parts[0], Integer.parseInt(parts[1]));
}
// note: isUnresolved check should be removed once we fix PeerAddress
if (addr != null && !addr.isUnresolved())
if (addr != null && !addr.isUnresolved()) {
peerAddressList.add(new PeerAddress(addr.getAddress(), addr.getPort()));
}
}
}
if (peerAddressList.size() > 0) {
@ -342,9 +371,8 @@ public class WalletService {
// could become outdated, so it is important that the user be able to
// disable it, but should be made aware of the reduced privacy.
if (socks5Proxy != null && !usePeerNodes) {
// SeedPeersSocks5Dns should replace SeedPeers once working reliably.
// SeedPeers uses hard coded stable addresses (from MainNetParams). It should be updated from time to time.
walletAppKit.setDiscovery(new SeedPeers(params));
walletAppKit.setDiscovery(new Socks5MultiDiscovery(socks5Proxy, params, socks5DiscoverMode));
}
walletAppKit.setDownloadListener(downloadListener)
@ -385,7 +413,9 @@ public class WalletService {
return "BitcoinJ wallet:\n" +
wallet.toString(includePrivKeys, true, true, walletAppKit.chain()) + "\n\n" +
"Bitsquare address entry list:\n" +
addressEntryListData.toString();
addressEntryListData.toString() +
"All pubkeys as hex:\n" +
wallet.printAllPubKeysAsHex();
}
public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
@ -1128,9 +1158,9 @@ public class WalletService {
}
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////////////////////
private static class DownloadListener extends DownloadProgressTracker {
private final DoubleProperty percentage = new SimpleDoubleProperty(-1);

View file

@ -68,6 +68,8 @@ public class BankUtil {
case "NZ":
case "AU":
case "SE":
case "CL":
case "NO":
return false;
case "CA":
case "MX":
@ -104,6 +106,7 @@ public class BankUtil {
case "MX":
case "HK":
case "SE":
case "NO":
return false;
default:
return true;
@ -146,10 +149,14 @@ public class BankUtil {
case "CA":
case "HK":
return "Account number:";
case "NO":
return "Kontonummer:";
case "SE":
return "Bankgiro number:";
case "MX":
return "CLABE:";
case "CL":
return "Cuenta:";
default:
return "Account no. (IBAN):";
}
@ -224,6 +231,7 @@ public class BankUtil {
case "MX":
case "HK":
case "SE":
case "NO":
return true;
default:
return false;

View file

@ -137,6 +137,10 @@ public class CountryUtil {
final Country country = new Country(locale.getCountry(), locale.getDisplayCountry(), region);
allCountries.add(country);
}
allCountries.add(new Country("GE", "Georgia", new Region("AS", getRegionName("AS"))));
allCountries.add(new Country("BW", "Botswana", new Region("AF", getRegionName("AF"))));
final List<Country> allCountriesList = new ArrayList<>(allCountries);
allCountriesList.sort((locale1, locale2) -> locale1.name.compareTo(locale2.name));
return allCountriesList;
@ -161,8 +165,8 @@ public class CountryUtil {
// other source of countries: https://developers.braintreepayments.com/reference/general/countries/java
private static final String[] countryCodes = new String[]{"AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH",
"BO", "BR", "BY", "CA", "CH", "CL", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ",
"EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN",
"BO", "BR", "BW", "BY", "CA", "CH", "CL", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ",
"EC", "EE", "EG", "ES", "FI", "FR", "GE", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN",
"IQ", "IS", "IT", "JO", "JP", "KE", "KH", "KR", "KW", "KZ", "LB", "LT", "LU", "LV", "LY", "MA", "MD", "ME", "MK", "MT", "MX",
"MY", "NI", "NL", "NO", "NZ", "OM", "PA", "PE", "PH", "PL", "PR", "PT", "PY", "QA", "RO", "RS", "RU",
"SA", "SD", "SE", "SG", "SI", "SK", "SV", "SY", "TH", "TN", "TR", "TW", "UA", "US", "UY", "VE", "VN",
@ -170,8 +174,8 @@ public class CountryUtil {
private static final List<String> countryCodeList = Arrays.asList(countryCodes);
private static final String[] regionCodes = new String[]{"AS", "EU", "SA", "EU", "OC", "EU", "EU", "EU", "AS",
"SA", "SA", "EU", "NA", "EU", "SA", "AS", "SA", "NA", "EU", "NA", "EU", "EU", "EU", "EU", "NA", "AF",
"SA", "EU", "AF", "EU", "EU", "EU", "EU", "EU", "NA", "AS", "NA", "EU", "EU", "AS", "EU", "AS", "AS",
"SA", "SA", "AF", "EU", "NA", "EU", "SA", "AS", "SA", "NA", "EU", "NA", "EU", "EU", "EU", "EU", "NA", "AF",
"SA", "EU", "AF", "EU", "EU", "EU", "AS", "EU", "EU", "NA", "AS", "NA", "EU", "EU", "AS", "EU", "AS", "AS",
"AS", "EU", "EU", "AS", "AS", "AF", "AS", "AS", "AS", "AS", "AS", "EU", "EU", "EU", "AF", "AF", "EU", "EU", "EU", "EU", "NA",
"AS", "NA", "EU", "EU", "OC", "AS", "NA", "SA", "AS", "EU", "NA", "EU", "SA", "AS", "EU", "EU", "EU",
"AS", "AF", "EU", "AS", "EU", "EU", "NA", "AS", "AS", "AF", "AS", "AS", "EU", "NA", "SA", "SA", "AS",

View file

@ -78,6 +78,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("AIB", "Advanced Internet Blocks"));
result.add(new CryptoCurrency("ANC", "Anoncoin"));
result.add(new CryptoCurrency("ANTI", "Anti"));
result.add(new CryptoCurrency("ARCO", "AquariusCoin"));
result.add(new CryptoCurrency("ARG", "Argentum"));
result.add(new CryptoCurrency("REP", "Augur", true));
result.add(new CryptoCurrency("BATL", "Battlestars"));
@ -91,6 +92,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("BITNZD", "BitNZD", true));
result.add(new CryptoCurrency("BITSEK", "BitSEK", true));
result.add(new CryptoCurrency("BITSGD", "BitSGD", true));
result.add(new CryptoCurrency("GBYTE", "Byte"));
result.add(new CryptoCurrency("SYNQ", "BitSYNQ"));
result.add(new CryptoCurrency("BTS", "BitShares"));
result.add(new CryptoCurrency("BITUSD", "BitUSD", true));
@ -103,7 +105,6 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("CRBIT", "Creditbit"));
result.add(new CryptoCurrency("CRW", "Crown"));
result.add(new CryptoCurrency("CBX", "Crypto Bullion"));
result.add(new CryptoCurrency("DAO", "DAO", true));
result.add(new CryptoCurrency("DNET", "DarkNet"));
result.add(new CryptoCurrency("DIBC", "DIBCOIN"));
result.add(new CryptoCurrency("DASH", "Dash"));
@ -132,9 +133,11 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("HODL", "HOdlcoin"));
result.add(new CryptoCurrency("HNC", "HunCoin"));
result.add(new CryptoCurrency("IOC", "I/O Coin"));
result.add(new CryptoCurrency("IOP", "Fermat"));
result.add(new CryptoCurrency("JPYT", "JPY Tether"));
result.add(new CryptoCurrency("JBS", "Jumbucks"));
result.add(new CryptoCurrency("LBC", "LBRY Credits"));
result.add(new CryptoCurrency("LTBC", "LTBcoin"));
result.add(new CryptoCurrency("LSK", "Lisk"));
result.add(new CryptoCurrency("LTC", "Litecoin"));
result.add(new CryptoCurrency("MAID", "MaidSafeCoin"));
@ -146,6 +149,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("XMY", "Myriadcoin"));
result.add(new CryptoCurrency("NAV", "Nav Coin"));
result.add(new CryptoCurrency("XEM", "NEM"));
result.add(new CryptoCurrency("NEVA", "Nevacoin"));
result.add(new CryptoCurrency("NMC", "Namecoin"));
result.add(new CryptoCurrency("NBT", "NuBits"));
result.add(new CryptoCurrency("NSR", "NuShares"));
@ -156,6 +160,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("PASC", "Pascal Coin"));
result.add(new CryptoCurrency("PPC", "Peercoin"));
result.add(new CryptoCurrency("PINK", "Pinkcoin"));
result.add(new CryptoCurrency("PIVX", "PIVX"));
result.add(new CryptoCurrency("XPTX", "PlatinumBar"));
result.add(new CryptoCurrency("PLU", "Plutons", true));
result.add(new CryptoCurrency("POST", "PostCoin"));
@ -165,6 +170,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("REALEST", "RealEst. Coin"));
result.add(new CryptoCurrency("RDD", "ReddCoin"));
result.add(new CryptoCurrency("XRP", "Ripple"));
result.add(new CryptoCurrency("SFSC", "Safe FileSystem Coin"));
result.add(new CryptoCurrency("STEEM", "STEEM"));
result.add(new CryptoCurrency("SDC", "ShadowCash"));
result.add(new CryptoCurrency("SHIFT", "Shift"));
@ -177,6 +183,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("XLM", "Stellar Lumens"));
result.add(new CryptoCurrency("SJCX", "StorjcoinX"));
result.add(new CryptoCurrency("STRAT", "Stratis"));
result.add(new CryptoCurrency("SWT", "Swarm City Token"));
result.add(new CryptoCurrency("SYNX", "Syndicate"));
result.add(new CryptoCurrency("AMP", "Synereo", true));
result.add(new CryptoCurrency("TRI", "Triangles"));
@ -188,6 +195,7 @@ public class CurrencyUtil {
result.add(new CryptoCurrency("VRC", "VeriCoin"));
result.add(new CryptoCurrency("WDC", "Worldcoin"));
result.add(new CryptoCurrency("WAVES", "Waves"));
result.add(new CryptoCurrency("XAUR", "Xaurum"));
result.add(new CryptoCurrency("YACC", "YACCoin"));
result.add(new CryptoCurrency("YBC", "YbCoin"));
result.add(new CryptoCurrency("ZEC", "Zcash"));

View file

@ -195,8 +195,8 @@ public class TradeManager {
private void initPendingTrades() {
Log.traceCall();
List<Trade> toAdd = new ArrayList<>();
List<Trade> toRemove = new ArrayList<>();
List<Trade> addTradeToFailedTradesList = new ArrayList<>();
List<Trade> removePreparedTradeList = new ArrayList<>();
tradesForStatistics = new ArrayList<>();
for (Trade trade : trades) {
trade.setStorage(tradableListStorage);
@ -206,16 +206,16 @@ public class TradeManager {
trade.updateDepositTxFromWallet();
tradesForStatistics.add(trade);
} else if (trade.isTakerFeePaid()) {
toAdd.add(trade);
addTradeToFailedTradesList.add(trade);
} else {
toRemove.add(trade);
removePreparedTradeList.add(trade);
}
}
for (Trade trade : toAdd)
for (Trade trade : addTradeToFailedTradesList)
addTradeToFailedTrades(trade);
for (Trade trade : toRemove)
for (Trade trade : removePreparedTradeList)
removePreparedTrade(trade);
for (Tradable tradable : closedTradableManager.getClosedTrades()) {

View file

@ -28,6 +28,7 @@ import io.bitsquare.common.handlers.ErrorMessageHandler;
import io.bitsquare.common.handlers.ResultHandler;
import io.bitsquare.common.util.JsonExclude;
import io.bitsquare.common.util.MathUtils;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.storage.payload.RequiresOwnerIsOnlinePayload;
@ -276,10 +277,6 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
return getVolumeByAmount(getMinAmount());
}
public String getReferenceText() {
return getId().substring(0, Math.min(8, getId().length()));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Availability
@ -352,7 +349,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
}
public String getShortId() {
return getId().substring(0, Math.min(8, getId().length()));
return Utilities.getShortId(id);
}
public NodeAddress getOffererNodeAddress() {

View file

@ -168,7 +168,12 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
public void removeAllOpenOffers(@Nullable Runnable completeHandler) {
removeOpenOffers(getOpenOffers(), completeHandler);
}
public void removeOpenOffers(List<OpenOffer> openOffers, @Nullable Runnable completeHandler) {
final int size = openOffers.size();
// Copy list as we remove in the loop
List<OpenOffer> openOffersList = new ArrayList<>(openOffers);
openOffersList.forEach(openOffer -> removeOpenOffer(openOffer, () -> {
}, errorMessage -> {
@ -444,8 +449,20 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
final long maxDelay = (i + 2) * delay;
final OpenOffer openOffer = openOffersList.get(i);
UserThread.runAfterRandomDelay(() -> {
if (openOffers.contains(openOffer) && openOffer.getId() != null)
republishOffer(openOffer);
if (openOffers.contains(openOffer)) {
// The openOffer.getId().contains("_") check is because there was once a version
// where we encoded the version nr in the offer id with a "_" as separator.
// That caused several issues and was reverted. So if there are still old offers out with that
// special offer ID format those must not be published as they cause failed taker attempts
// with lost taker fee.
String id = openOffer.getId();
if (id != null && !id.contains("_"))
republishOffer(openOffer);
else
log.warn("You have an offer with an invalid offer ID: offerID=" + id);
}
}, minDelay, maxDelay, TimeUnit.MILLISECONDS);
}
} else {

View file

@ -37,7 +37,7 @@ import static io.bitsquare.util.Validator.nonEmptyStringOf;
public class OfferAvailabilityProtocol {
private static final Logger log = LoggerFactory.getLogger(OfferAvailabilityProtocol.class);
private static final long TIMEOUT_SEC = 45;
private static final long TIMEOUT_SEC = 60;
private final OfferAvailabilityModel model;
private final ResultHandler resultHandler;

View file

@ -37,7 +37,7 @@ import static io.bitsquare.util.Validator.nonEmptyStringOf;
public abstract class TradeProtocol {
private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class);
private static final long TIMEOUT_SEC = 60;
private static final long TIMEOUT_SEC = 75;
protected final ProcessModel processModel;
private final DecryptedDirectMessageListener decryptedDirectMessageListener;
@ -110,7 +110,7 @@ public abstract class TradeProtocol {
stopTimeout();
timeoutTimer = UserThread.runAfter(() -> {
log.error("Timeout reached");
log.error("Timeout reached. TradeID=" + trade.getId());
trade.setErrorMessage("A timeout occurred.");
cleanupTradable();
cleanup();

View file

@ -61,7 +61,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
buyerMultiSigAddressEntry.setLockedTradeAmount(Coin.valueOf(buyerInputs.stream().mapToLong(input -> input.value).sum()).subtract(FeePolicy.getFixedTxFeeForTrades()));
walletService.saveAddressEntryList();
TradingPeer tradingPeer = processModel.tradingPeer;
processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
Transaction depositTx = processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
false,
contractHash,
processModel.getPreparedDepositTx(),
@ -86,6 +86,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
failed(t);
}
});
trade.setDepositTx(depositTx);
} catch (Throwable t) {
failed(t);
}

View file

@ -60,7 +60,7 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask {
sellerMultiSigAddressEntry.setLockedTradeAmount(Coin.valueOf(sellerInputs.stream().mapToLong(input -> input.value).sum()).subtract(FeePolicy.getFixedTxFeeForTrades()));
walletService.saveAddressEntryList();
TradingPeer tradingPeer = processModel.tradingPeer;
processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
Transaction depositTx = processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(
true,
contractHash,
processModel.getPreparedDepositTx(),
@ -85,6 +85,7 @@ public class SignAndPublishDepositTxAsSeller extends TradeTask {
failed(t);
}
});
trade.setDepositTx(depositTx);
} catch (Throwable t) {
failed(t);
}

View file

@ -0,0 +1,50 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.alert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class AlertTest {
private static final Logger log = LoggerFactory.getLogger(AlertTest.class);
@Test
public void testIsNewVersion() {
Alert alert = new Alert(null, true, "0.4.9.9.1");
assertTrue(alert.isNewVersion("0.4.9.9"));
assertTrue(alert.isNewVersion("0.4.9.8"));
assertTrue(alert.isNewVersion("0.4.9"));
assertTrue(alert.isNewVersion("0.4.9.9.0"));
assertFalse(alert.isNewVersion("0.4.9.9.1"));
alert = new Alert(null, true, "0.4.9.9.2");
assertTrue(alert.isNewVersion("0.4.9.9.1"));
assertFalse(alert.isNewVersion("0.4.9.9.2"));
assertTrue(alert.isNewVersion("0.4.9.8"));
assertTrue(alert.isNewVersion("0.4.9"));
alert = new Alert(null, true, "0.4.9.9");
assertTrue(alert.isNewVersion("0.4.9"));
assertTrue(alert.isNewVersion("0.4.9.8"));
assertFalse(alert.isNewVersion("0.4.9.9"));
}
}

View file

@ -13,16 +13,16 @@ To contribute a patch, the workflow is as follows:
Please do not make pull requests to the Master branch but always use the Development branch!
Copyright
Workflow for merging code
---------
We are aiming to follow the principles and establish the workflow used at the Bitcoin Core project.
Please check out the [Bitcoin Core documentation](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md) for more info.
The full workflow is not used yet as there are not enough dedicated developers, but as soon that situation changes we will move
The full workflow is not used yet as there are not that many dedicated developers yet, but as soon that situation changes we will move
to a model with ACK/NACK, code reviews and maintainer roles.
Copyright
---------
By contributing to this repository, you agree to license your work under the AGPL license.
Please include the license header into your files.
Please include the Bitsquare license header into your files.

View file

@ -13,11 +13,13 @@ System requirements
The prerequisite for building Bitsquare is installing the Java Development Kit (JDK), version 8u112 or better (as well as maven and git).
In Debian/Ubuntu systems with OpenJDK you'll need OpenJFX as well, i.e. you'll need the `openjfx` package besides the `openjdk-8-jdk` package.
$ sudo apt-get install openjdk-8-jdk maven libopenjfx-java
### 1. Check the version of Java you currently have installed
$ java -version
If `java` is not found, or your version is anything less than `1.8.0_112`, then follow the next steps, otherwise you can skip to step 2:
If `java` is not found, or your version is anything less than `1.8.0_121`, then follow the next steps, otherwise you can skip to step 2:
#### 1.1 Debian based systems (Ubuntu)

View file

@ -10,12 +10,12 @@ add-apt-repository ppa:webupd8team/java
apt-get update
apt-get -y install oracle-java8-installer git maven unzip
# Alternatively you can download the latest jdk and extract it to /usr/lib/jvm/jdk1.8.0_112
# Alternatively you can download the latest jdk and extract it to $JAVA_HOME
# wget http://download.oracle.com/otn-pub/java/jdk/8u112-b15/jdk-8u112-linux-x64.tar.gz --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie"
# If you had an older java version installed set the new java version as default by those commands:
apt-get install update-alternatives
# update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_112/bin/java 2000
# update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0_112/bin/javac 2000
# update-alternatives --install /usr/bin/java java $JAVA_HOME/bin/java 2000
# update-alternatives --install /usr/bin/javac javac $JAVA_HOME/bin/javac 2000
# Test with java -version and javac- version if the version is correct. Otherwise check here:
# sudo update-alternatives --config java
# sudo update-alternatives --config javac
@ -23,16 +23,15 @@ apt-get install update-alternatives
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
apt-get install unzip
#apt-get install unzip
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
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
chmod 777 /usr/lib/jvm/jdk1.8.0_112/jre/lib/security/US_export_policy.jar
chmod 777 /usr/lib/jvm/jdk1.8.0_112/jre/lib/security/local_policy.jar
sudo chmod 777 $JAVA_HOME/jre/lib/security/US_export_policy.jar
sudo chmod 777 $JAVA_HOME/jre/lib/security/local_policy.jar
rm -r UnlimitedJCEPolicyJDK8
rm jce_policy-8.zip
rm -r UnlimitedJCEPolicyJDK8 jce_policy-8.zip
echo "Install bitcoinj"
cd ~
@ -48,6 +47,6 @@ mvn clean package -DskipTests -Dmaven.javadoc.skip=true
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
sudo cp .m2/repository/org/bouncycastle/bcprov-jdk15on/1.53/bcprov-jdk15on-1.53.jar $JAVA_HOME/jre/lib/ext/bcprov-jdk15on-1.53.jar

View file

@ -22,7 +22,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>io.bitsquare</groupId>
<version>0.4.9.8</version>
<version>0.4.9.9</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -89,24 +89,204 @@
</executions>
</plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<DependencyConvergence />
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>-->
<!-- Verify the dependency chain: see https://github.com/gary-rowe/BitcoinjEnforcerRules for
more information on this.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<phase>verify</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<DependencyConvergence/>
<digestRule implementation="uk.co.froot.maven.enforcer.DigestRule">
<!-- Create a snapshot to build the list of URNs below -->
<buildSnapshot>true</buildSnapshot>
<!-- List of required hashes -->
<!-- Format is URN of groupId:artifactId:version:type:classifier:scope:hash -->
<!-- classifier is "null" if not present -->
<urns>
<urn>
aopalliance:aopalliance:1.0:jar:null:compile:0235ba8b489512805ac13a8f9ea77a1ca5ebe3e8
</urn>
<urn>
ch.qos.logback:logback-classic:1.1.10:jar:null:compile:56d094f1fd93c2578e8138a83bb036c13abe5898
</urn>
<urn>
ch.qos.logback:logback-core:1.1.10:jar:null:compile:ee683b13d3ca1cdc876cdf8a341c81d04976e1d6
</urn>
<urn>
com.google.code.findbugs:jsr305:3.0.1:jar:null:compile:f7be08ec23c21485b9b5a1cf1654c2ec8c58168d
</urn>
<urn>
com.google.code.gson:gson:2.2.4:jar:null:compile:a60a5e993c98c864010053cb901b7eab25306568
</urn>
<urn>
com.google.guava:guava:18.0:jar:null:compile:cce0823396aa693798f8882e64213b1772032b09
</urn>
<urn>
com.google.inject:guice:3.0:jar:null:compile:9d84f15fe35e2c716a02979fb62f50a29f38aefa
</urn>
<urn>
com.google.protobuf:protobuf-java:2.5.0:jar:null:compile:a10732c76bfacdbd633a7eb0f7968b1059a65dfa
</urn>
<urn>
com.google.zxing:core:2.0:jar:null:compile:001a5b8ccf93ca2fb7c40a94417f8485e3c8b4a6
</urn>
<urn>
com.google.zxing:javase:2.0:jar:null:compile:d6384ed133a5d2ae38b9cdece13a56564f91066e
</urn>
<urn>
com.googlecode.jcsv:jcsv:1.4.0:jar:null:compile:3b2dfd1ff251cdcf4745a7643a966f14d10e2532
</urn>
<urn>
com.lambdaworks:scrypt:1.4.0:jar:null:compile:906506b74f30c8c20bccd9ed4a11112d8941fe87
</urn>
<urn>
com.madgag.spongycastle:core:1.51.0.0:jar:null:compile:0f642963312ea0e615ad65f28adc5a5b3a2a0862
</urn>
<urn>
com.squareup.okhttp:okhttp:2.2.0:jar:null:compile:959c454243581fdf730abfd4f4745441724bcf2c
</urn>
<urn>
com.squareup.okio:okio:1.2.0:jar:null:compile:c0b52915a48fa91b1b94a28d4a2997bac5f524df
</urn>
<urn>
commons-codec:commons-codec:1.9:jar:null:compile:9ce04e34240f674bc72680f8b843b1457383161a
</urn>
<urn>
commons-io:commons-io:2.4:jar:null:compile:b1b6ea3b7e4aa4f492509a4952029cd8e48019ad
</urn>
<urn>
commons-logging:commons-logging:1.1.3:jar:null:compile:f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f
</urn>
<urn>
de.jensd:fontawesomefx:8.0.0:jar:null:compile:b42d82b8889fb77858d1f17e69dbdc24beb2ad25
</urn>
<urn>
javax.inject:javax.inject:1:jar:null:compile:6975da39a7040257bd51d21a231b76c915872d38
</urn>
<urn>junit:junit:4.11:jar:null:test:4e031bb61df09069aeb2bffb4019e7a5034a4ee0
</urn>
<urn>
net.bytebuddy:byte-buddy-agent:1.6.5:jar:null:test:1a1e2189a7dcca6e861a4d71ffbda621f74072d6
</urn>
<urn>
net.bytebuddy:byte-buddy:1.6.5:jar:null:test:25cf61dea06b7402a91bd8435af4ecfc0dd5935b
</urn>
<urn>
net.glxn:qrgen:1.3:jar:null:compile:e581726a5bffa3a5d624506ae145acbac3cc4a17
</urn>
<urn>
net.jcip:jcip-annotations:1.0:jar:null:compile:afba4942caaeaf46aab0b976afd57cc7c181467e
</urn>
<urn>
net.sf.jopt-simple:jopt-simple:4.8:jar:null:compile:457ac8fb446301588580604fa877cc56285ff74a
</urn>
<urn>
org.apache.commons:commons-lang3:3.4:jar:null:compile:5fe28b9518e58819180a43a850fbc0dd24b7c050
</urn>
<urn>
org.apache.httpcomponents:httpclient:4.5:jar:null:compile:a1e6cbb3cc2c5f210dd1310ff9fcb2c09c0d1438
</urn>
<urn>
org.apache.httpcomponents:httpcore:4.4.1:jar:null:compile:f5aa318bda4c6c8d688c9d00b90681dcd82ce636
</urn>
<urn>
org.apache.maven.plugins:maven-clean-plugin:2.5:maven-plugin:null:runtime:75653decaefa85ca8114ff3a4f869bb2ee6d605d
</urn>
<urn>
org.apache.maven.plugins:maven-compiler-plugin:3.1:maven-plugin:null:runtime:9977a8d04e75609cf01badc4eb6a9c7198c4c5ea
</urn>
<urn>
org.apache.maven.plugins:maven-deploy-plugin:2.7:maven-plugin:null:runtime:6dadfb75679ca010b41286794f737088ebfe12fd
</urn>
<urn>
org.apache.maven.plugins:maven-enforcer-plugin:RELEASE:maven-plugin:null:runtime:e9bd7df541415bfe587ce082458f9a48bf9b55b4
</urn>
<urn>
org.apache.maven.plugins:maven-install-plugin:2.4:maven-plugin:null:runtime:9d1316166fe4c313f56276935e08df11f45267c2
</urn>
<urn>
org.apache.maven.plugins:maven-jar-plugin:2.4:maven-plugin:null:runtime:e3200bcf357b5c5e26df072d27df160546bb079a
</urn>
<urn>
org.apache.maven.plugins:maven-resources-plugin:2.6:maven-plugin:null:runtime:dd093ff6a4b680eae7ae83b5ab04310249fc6590
</urn>
<urn>
org.apache.maven.plugins:maven-shade-plugin:2.3:maven-plugin:null:runtime:d136adc7abccc9c12adcad6ae7a9bc51b2b7184b
</urn>
<urn>
org.apache.maven.plugins:maven-site-plugin:3.3:maven-plugin:null:runtime:77ba1752b1ac4c4339d6f11554800960a56a4ae1
</urn>
<urn>
org.apache.maven.plugins:maven-surefire-plugin:2.12.4:maven-plugin:null:runtime:2b435f7f77777d2e62354fdc690da3f1dc47a26b
</urn>
<urn>
org.bitcoinj:orchid:1.1.1:jar:null:compile:7898329eae76ec6bfdf27081234bb222d5be09df
</urn>
<urn>
org.bouncycastle:bcprov-jdk15on:1.53:jar:null:compile:9d3def2fa5a0d2ed0c1146e9945df10d29eb4ccb
</urn>
<urn>
org.controlsfx:controlsfx:8.0.6_20:jar:null:compile:5a4ca2765419fe12af0f0c7c5a8129c53bb661d9
</urn>
<urn>
org.fxmisc.easybind:easybind:1.0.3:jar:null:compile:336c8226dfa775c714bc8c3410a1565feffcfb34
</urn>
<urn>
org.hamcrest:hamcrest-core:1.3:jar:null:test:42a25dc3219429f0e5d060061f71acb49bf010a0
</urn>
<urn>
org.jetbrains:annotations:13.0:jar:null:compile:919f0dfe192fb4e063e7dacadee7f8bb9a2672a9
</urn>
<urn>
org.mockito:mockito-core:2.7.5:jar:null:test:24f0d023df553036db3e0cf949d1c4748b84bd69
</urn>
<urn>
org.objenesis:objenesis:2.5:jar:null:test:612ecb799912ccf77cba9b3ed8c813da086076e9
</urn>
<urn>
org.slf4j:slf4j-api:1.7.22:jar:null:compile:a1c83373863cec7ae8d89dc1c5722d8cb6ec0309
</urn>
<urn>
org.springframework:spring-core:4.1.1.RELEASE:jar:null:compile:4b0c607ba83c95ad9ea3defd40c35ae47c5df7d0
</urn>
<urn>
org.springframework:spring-test:4.1.1.RELEASE:jar:null:test:406ce9c05253f7dd75ac3f31170c71cca7419d8a
</urn>
<!-- A check for the rules themselves -->
<urn>
uk.co.froot.maven.enforcer:digest-enforcer-rules:0.0.1:jar:null:runtime:16a9e04f3fe4bb143c42782d07d5faf65b32106f
</urn>
</urns>
</digestRule>
</rules>
</configuration>
</execution>
</executions>
<!-- Ensure we download the enforcer rules -->
<dependencies>
<dependency>
<groupId>uk.co.froot.maven.enforcer</groupId>
<artifactId>digest-enforcer-rules</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

View file

@ -23,6 +23,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import io.bitsquare.alert.AlertManager;
import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.common.UserThread;
@ -41,10 +42,7 @@ import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.MainViewModel;
import io.bitsquare.gui.main.debug.DebugView;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.EmptyWalletWindow;
import io.bitsquare.gui.main.overlays.windows.FilterWindow;
import io.bitsquare.gui.main.overlays.windows.SendAlertMessageWindow;
import io.bitsquare.gui.main.overlays.windows.ShowWalletDataWindow;
import io.bitsquare.gui.main.overlays.windows.*;
import io.bitsquare.gui.util.ImageUtil;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.storage.Storage;
@ -129,6 +127,9 @@ public class BitsquareApp extends Application {
if (throwable.getCause() != null && throwable.getCause().getCause() != null &&
throwable.getCause().getCause() instanceof BlockStoreException) {
log.error(throwable.getMessage());
} else if (throwable instanceof ClassCastException &&
"sun.awt.image.BufImgSurfaceData cannot be cast to sun.java2d.xr.XRSurfaceData".equals(throwable.getMessage())) {
log.warn(throwable.getMessage());
} else {
log.error("Uncaught Exception from thread " + Thread.currentThread().getName());
log.error("throwableMessage= " + throwable.getMessage());
@ -197,27 +198,33 @@ public class BitsquareApp extends Application {
stop();
});
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEvent -> {
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN).match(keyEvent)) {
stop();
} else if (new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN).match(keyEvent)) {
stop();
} else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN).match(keyEvent)) {
showEmptyWalletPopup();
} else if (new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.M, KeyCombination.ALT_DOWN).match(keyEvent)) {
showSendAlertMessagePopup();
} else if (new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.F, KeyCombination.ALT_DOWN).match(keyEvent)) {
showFilterPopup();
} else if (new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.F, KeyCombination.ALT_DOWN).match(keyEvent)) {
showFPSWindow();
} else if (new KeyCodeCombination(KeyCode.J, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
} else if (new KeyCodeCombination(KeyCode.J, KeyCombination.ALT_DOWN).match(keyEvent)) {
WalletService walletService = injector.getInstance(WalletService.class);
if (walletService.getWallet() != null)
new ShowWalletDataWindow(walletService).information("Wallet raw data").show();
else
new Popup<>().warning("The wallet is not initialized yet").show();
} else if (DevFlags.DEV_MODE) {
if (new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent))
showDebugWindow();
} else if (new KeyCodeCombination(KeyCode.G, KeyCombination.ALT_DOWN).match(keyEvent)) {
TradeWalletService tradeWalletService = injector.getInstance(TradeWalletService.class);
WalletService walletService = injector.getInstance(WalletService.class);
if (walletService.getWallet() != null)
new SpendFromDepositTxWindow(tradeWalletService).information("Emergency wallet tool").show();
else
new Popup<>().warning("The wallet is not initialized yet").show();
} else if (DevFlags.DEV_MODE && new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
showDebugWindow();
}
});
@ -256,9 +263,14 @@ public class BitsquareApp extends Application {
UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES);
} catch (Throwable throwable) {
} catch (
Throwable throwable
)
{
showErrorPopup(throwable, false);
}
}
private void showSendAlertMessagePopup() {

View file

@ -119,6 +119,7 @@ public class CryptoCurrencyForm extends PaymentMethodForm {
@Override
public void updateAllInputsValid() {
altCoinAddressValidator.setCurrencyCode(cryptoCurrencyAccount.getSelectedTradeCurrency().getCode());
allInputsValid.set(isAccountNameValid()
&& altCoinAddressValidator.validate(cryptoCurrencyAccount.getAddress()).isValid
&& cryptoCurrencyAccount.getSingleTradeCurrency() != null);

View file

@ -46,7 +46,7 @@ public class InteracETransferForm extends PaymentMethodForm {
public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountContractData paymentAccountContractData) {
addLabelTextField(gridPane, ++gridRow, "Account holder name:", ((InteracETransferAccountContractData) paymentAccountContractData).getHolderName());
addLabelTextField(gridPane, ++gridRow, "Email:", ((InteracETransferAccountContractData) paymentAccountContractData).getEmail());
addLabelTextField(gridPane, ++gridRow, "Email or mobile nr:", ((InteracETransferAccountContractData) paymentAccountContractData).getEmail());
addLabelTextField(gridPane, ++gridRow, "Secret question:", ((InteracETransferAccountContractData) paymentAccountContractData).getQuestion());
addLabelTextField(gridPane, ++gridRow, "Answer:", ((InteracETransferAccountContractData) paymentAccountContractData).getAnswer());
return gridRow;
@ -69,7 +69,7 @@ public class InteracETransferForm extends PaymentMethodForm {
updateFromInputs();
});
mobileNrInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Email:").second;
mobileNrInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Email or mobile nr:").second;
mobileNrInputTextField.setValidator(interacETransferValidator);
mobileNrInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
interacETransferAccount.setEmail(newValue);
@ -77,14 +77,14 @@ public class InteracETransferForm extends PaymentMethodForm {
});
questionInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Secret question:").second;
questionInputTextField.setValidator(interacETransferValidator);
questionInputTextField.setValidator(inputValidator);
questionInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
interacETransferAccount.setQuestion(newValue);
updateFromInputs();
});
answerInputTextField = addLabelInputTextField(gridPane, ++gridRow, "Answer:").second;
answerInputTextField.setValidator(interacETransferValidator);
answerInputTextField.setValidator(inputValidator);
answerInputTextField.textProperty().addListener((ov, oldValue, newValue) -> {
interacETransferAccount.setAnswer(newValue);
updateFromInputs();

View file

@ -237,8 +237,7 @@ public class MainViewModel implements ViewModel {
}
private void initializeAllServices() {*/
log.error("initializeAllServices");
Log.traceCall();
UserThread.runAfter(tacWindow::showIfNeeded, 2);
@ -638,6 +637,30 @@ public class MainViewModel implements ViewModel {
.show();
}
});
checkIfOpenOffersMatchTradeProtocolVersion();
}
private void checkIfOpenOffersMatchTradeProtocolVersion() {
List<OpenOffer> outDatedOffers = openOfferManager.getOpenOffers()
.stream()
.filter(e -> e.getOffer().getProtocolVersion() != Version.TRADE_PROTOCOL_VERSION)
.collect(Collectors.toList());
if (!outDatedOffers.isEmpty()) {
new Popup<>()
.warning("You have open offers which have been created with an older version of Bitsquare.\n" +
"Please remove those offers as they are not valid anymore.\n\n" +
"Offers (ID): " +
outDatedOffers.stream()
.map(e -> e.getId() + "\n")
.collect(Collectors.toList()).toString()
.replace("[", "").replace("]", ""))
.actionButtonText("Remove outdated offer(s)")
.onAction(() -> openOfferManager.removeOpenOffers(outDatedOffers, null))
.closeButtonText("Shut down")
.onClose(BitsquareApp.shutDownHandler::run)
.show();
}
}

View file

@ -79,7 +79,7 @@ public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
};
keyEventEventHandler = event -> {
if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event) &&
if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event) &&
arbitratorRegistrationTab == null) {
arbitratorRegistrationTab = new Tab("Arbitrator registration");
arbitratorRegistrationTab.setClosable(false);

View file

@ -164,6 +164,16 @@ public class AltCoinAccountsView extends ActivatableViewAndModel<GridPane, AltCo
"If you are not sure about that process visit the Monero forum (https://forum.getmonero.org) to find more information.")
.closeButtonText("I understand")
.show();
} else if (code.equals("ZEC")) {
new Popup().information("When using ZEC you can only use the transparent addresses (starting with t) not " +
"the z-addresses, because the arbitrator would not be able to verify the transaction with z-addresses.")
.closeButtonText("I understand")
.show();
} else if (code.equals("XZC")) {
new Popup().information("When using XZC you can only use the transparent transactions not " +
"the private transactions, because the arbitrator would not be able to verify the private transactions.")
.closeButtonText("I understand")
.show();
}
if (!model.getPaymentAccounts().stream().filter(e -> {

View file

@ -20,6 +20,7 @@ package io.bitsquare.gui.main.disputes;
import io.bitsquare.app.DevFlags;
import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.Activatable;
@ -51,6 +52,7 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
private final Navigation navigation;
private final ArbitratorManager arbitratorManager;
private DisputeManager disputeManager;
private final KeyRing keyRing;
private Preferences preferences;
@ -59,14 +61,15 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
private Tab currentTab;
private final ViewLoader viewLoader;
private MapChangeListener<NodeAddress, Arbitrator> arbitratorMapChangeListener;
private boolean isArbitrator;
@Inject
public DisputesView(CachingViewLoader viewLoader, Navigation navigation, ArbitratorManager arbitratorManager,
public DisputesView(CachingViewLoader viewLoader, Navigation navigation,
ArbitratorManager arbitratorManager, DisputeManager disputeManager,
KeyRing keyRing, Preferences preferences) {
this.viewLoader = viewLoader;
this.navigation = navigation;
this.arbitratorManager = arbitratorManager;
this.disputeManager = disputeManager;
this.keyRing = keyRing;
@ -92,11 +95,15 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
}
private void updateArbitratorsDisputesTabDisableState() {
isArbitrator = arbitratorManager.getArbitratorsObservableMap().values().stream()
boolean isActiveArbitrator = arbitratorManager.getArbitratorsObservableMap().values().stream()
.filter(e -> e.getPubKeyRing() != null && e.getPubKeyRing().equals(keyRing.getPubKeyRing()))
.findAny().isPresent();
if (arbitratorsDisputesTab == null && isArbitrator) {
boolean hasDisputesAsArbitrator = disputeManager.getDisputesAsObservableList().stream()
.filter(d -> d.getArbitratorPubKeyRing().equals(keyRing.getPubKeyRing()))
.findAny().isPresent();
if (arbitratorsDisputesTab == null && (isActiveArbitrator || hasDisputesAsArbitrator)) {
arbitratorsDisputesTab = new Tab("Arbitrator's support tickets");
arbitratorsDisputesTab.setClosable(false);
root.getTabs().add(arbitratorsDisputesTab);

View file

@ -18,7 +18,6 @@
package io.bitsquare.gui.main.disputes.arbitrator;
import io.bitsquare.alert.PrivateNotificationManager;
import io.bitsquare.arbitration.Dispute;
import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.gui.common.view.FxmlView;
@ -29,7 +28,6 @@ import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.trade.TradeManager;
import javafx.collections.transformation.FilteredList;
import javafx.stage.Stage;
import javax.inject.Inject;
@ -47,9 +45,27 @@ public class ArbitratorDisputeView extends TraderDisputeView {
}
@Override
protected void setFilteredListPredicate(FilteredList<Dispute> filteredList) {
filteredList.setPredicate(dispute -> dispute.getArbitratorPubKeyRing().equals(keyRing.getPubKeyRing()));
public void initialize() {
super.initialize();
filterBox.setVisible(true);
filterBox.setManaged(true);
}
@Override
protected void applyFilteredListPredicate(String filterString) {
// If in arbitrator view we must only display disputes where we are selected as arbitrator (must not receive others anyway)
filteredList.setPredicate(dispute ->
dispute.getArbitratorPubKeyRing().equals(keyRing.getPubKeyRing()) &&
(filterString.isEmpty() ||
(dispute.getId().contains(filterString) ||
formatter.formatDate(dispute.getOpeningDate()).contains(filterString)) ||
getBuyerOnionAddressColumnLabel(dispute).contains(filterString) ||
getSellerOnionAddressColumnLabel(dispute).contains(filterString)
));
}
}

View file

@ -21,6 +21,7 @@ import com.google.common.io.ByteStreams;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.alert.PrivateNotificationManager;
import io.bitsquare.app.Version;
import io.bitsquare.arbitration.Dispute;
import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.arbitration.messages.DisputeCommunicationMessage;
@ -34,6 +35,7 @@ import io.bitsquare.gui.common.view.ActivatableView;
import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.components.BusyAnimation;
import io.bitsquare.gui.components.HyperlinkWithIcon;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.components.TableGroupHeadline;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.main.overlays.windows.ContractWindow;
@ -92,7 +94,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
protected final KeyRing keyRing;
private final TradeManager tradeManager;
private final Stage stage;
private final BSFormatter formatter;
protected final BSFormatter formatter;
private final DisputeSummaryWindow disputeSummaryWindow;
private PrivateNotificationManager privateNotificationManager;
private final ContractWindow contractWindow;
@ -124,6 +126,10 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
private Subscription inputTextAreaTextSubscription;
private EventHandler<KeyEvent> keyEventEventHandler;
private Scene scene;
protected FilteredList<Dispute> filteredList;
private InputTextField filterTextField;
private ChangeListener<String> filterTextFieldListener;
protected HBox filterBox;
///////////////////////////////////////////////////////////////////////////////////////////
@ -148,10 +154,24 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
@Override
public void initialize() {
Label label = new Label("Filter list:");
HBox.setMargin(label, new Insets(5, 0, 0, 0));
filterTextField = new InputTextField();
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
filterBox = new HBox();
filterBox.setSpacing(5);
filterBox.getChildren().addAll(label, filterTextField);
VBox.setVgrow(filterBox, Priority.NEVER);
filterBox.setVisible(false);
filterBox.setManaged(false);
tableView = new TableView<>();
VBox.setVgrow(tableView, Priority.SOMETIMES);
tableView.setMinHeight(150);
root.getChildren().add(tableView);
root.getChildren().addAll(filterBox, tableView);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
Label placeholder = new Label("There are no open tickets");
placeholder.setWrapText(true);
@ -208,7 +228,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
disputeDirectMessageListListener = c -> scrollToBottom();
keyEventEventHandler = event -> {
if (new KeyCodeCombination(KeyCode.L, KeyCombination.SHORTCUT_DOWN).match(event)) {
if (new KeyCodeCombination(KeyCode.L, KeyCombination.ALT_DOWN).match(event)) {
Map<String, List<Dispute>> map = new HashMap<>();
disputeManager.getDisputesAsObservableList().stream().forEach(dispute -> {
String tradeId = dispute.getTradeId();
@ -269,14 +289,14 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
.actionButtonText("Copy")
.onAction(() -> Utilities.copyToClipboard(message))
.show();
} else if (new KeyCodeCombination(KeyCode.U, KeyCombination.SHORTCUT_DOWN).match(event)) {
} else if (new KeyCodeCombination(KeyCode.U, KeyCombination.ALT_DOWN).match(event)) {
// Hidden shortcut to re-open a dispute. Allow it also for traders not only arbitrator.
if (selectedDispute != null) {
if (selectedDisputeClosedPropertyListener != null)
selectedDispute.isClosedProperty().removeListener(selectedDisputeClosedPropertyListener);
selectedDispute.setIsClosed(false);
}
} else if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event)) {
} else if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) {
if (selectedDispute != null) {
PubKeyRing pubKeyRing = selectedDispute.getTraderPubKeyRing();
NodeAddress nodeAddress;
@ -295,10 +315,11 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
filterTextField.textProperty().addListener(filterTextFieldListener);
disputeManager.cleanupDisputes();
FilteredList<Dispute> filteredList = new FilteredList<>(disputeManager.getDisputesAsObservableList());
setFilteredListPredicate(filteredList);
filteredList = new FilteredList<>(disputeManager.getDisputesAsObservableList());
applyFilteredListPredicate(filterTextField.getText());
sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
@ -320,6 +341,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
@Override
protected void deactivate() {
filterTextField.textProperty().removeListener(filterTextFieldListener);
sortedList.comparatorProperty().unbind();
selectedDisputeSubscription.unsubscribe();
removeListenersOnSelectDispute();
@ -328,7 +350,8 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
protected void setFilteredListPredicate(FilteredList<Dispute> filteredList) {
protected void applyFilteredListPredicate(String filterString) {
// If in trader view we must not display arbitrators own disputes as trader (must not happen anyway)
filteredList.setPredicate(dispute -> !dispute.getArbitratorPubKeyRing().equals(keyRing.getPubKeyRing()));
}
@ -393,8 +416,17 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
}
private void onCloseDispute(Dispute dispute) {
disputeSummaryWindow.onFinalizeDispute(() -> messagesAnchorPane.getChildren().remove(messagesInputBox))
.show(dispute);
long protocolVersion = dispute.getContract().offer.getProtocolVersion();
if (protocolVersion == Version.TRADE_PROTOCOL_VERSION) {
disputeSummaryWindow.onFinalizeDispute(() -> messagesAnchorPane.getChildren().remove(messagesInputBox))
.show(dispute);
} else {
new Popup<>()
.warning("The offer in that dispute has been created with an older version of Bitsquare.\n" +
"You cannot close that dispute with your version of the application.\n\n" +
"Please use an older version with protocol version " + protocolVersion)
.show();
}
}
private void onRequestUpload() {
@ -489,7 +521,8 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
messageListView.prefWidthProperty().bind(root.widthProperty());
messagesAnchorPane.prefWidthProperty().bind(root.widthProperty());
disputeCommunicationMessages.addListener(disputeDirectMessageListListener);
selectedDispute.isClosedProperty().addListener(selectedDisputeClosedPropertyListener);
if (selectedDispute != null)
selectedDispute.isClosedProperty().addListener(selectedDisputeClosedPropertyListener);
inputTextAreaTextSubscription = EasyBind.subscribe(inputTextArea.textProperty(), t -> sendButton.setDisable(t.isEmpty()));
}
}
@ -497,8 +530,8 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
private void onSelectDispute(Dispute dispute) {
removeListenersOnSelectDispute();
if (dispute == null) {
if (root.getChildren().size() > 1)
root.getChildren().remove(1);
if (root.getChildren().size() > 2)
root.getChildren().remove(2);
selectedDispute = null;
} else if (selectedDispute != dispute) {
@ -805,9 +838,9 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
}
});
if (root.getChildren().size() > 1)
root.getChildren().remove(1);
root.getChildren().add(1, messagesAnchorPane);
if (root.getChildren().size() > 2)
root.getChildren().remove(2);
root.getChildren().add(2, messagesAnchorPane);
scrollToBottom();
}
@ -1022,7 +1055,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
}
private String getBuyerOnionAddressColumnLabel(Dispute item) {
protected String getBuyerOnionAddressColumnLabel(Dispute item) {
Contract contract = item.getContract();
if (contract != null) {
NodeAddress buyerNodeAddress = contract.getBuyerNodeAddress();
@ -1035,7 +1068,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
}
}
private String getSellerOnionAddressColumnLabel(Dispute item) {
protected String getSellerOnionAddressColumnLabel(Dispute item) {
Contract contract = item.getContract();
if (contract != null) {
NodeAddress sellerNodeAddress = contract.getSellerNodeAddress();

View file

@ -184,9 +184,9 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
};
keyEventEventHandler = event -> {
if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event))
if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event))
revertTxColumn.setVisible(!revertTxColumn.isVisible());
else if (new KeyCodeCombination(KeyCode.A, KeyCombination.SHORTCUT_DOWN).match(event))
else if (new KeyCodeCombination(KeyCode.A, KeyCombination.ALT_DOWN).match(event))
showStatisticsPopup();
};

View file

@ -15,14 +15,16 @@ public class SpreadItem {
public final int numberOfOffers;
@Nullable
public final Fiat spread;
public final String percentage;
public final Coin totalAmount;
public SpreadItem(String currencyCode, int numberOfBuyOffers, int numberOfSellOffers, int numberOfOffers, @Nullable Fiat spread, Coin totalAmount) {
public SpreadItem(String currencyCode, int numberOfBuyOffers, int numberOfSellOffers, int numberOfOffers, @Nullable Fiat spread, String percentage, Coin totalAmount) {
this.currencyCode = currencyCode;
this.numberOfBuyOffers = numberOfBuyOffers;
this.numberOfSellOffers = numberOfSellOffers;
this.numberOfOffers = numberOfOffers;
this.spread = spread;
this.percentage = percentage;
this.totalAmount = totalAmount;
}
}

View file

@ -286,7 +286,7 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode
super.updateItem(item, empty);
if (item != null && !empty) {
if (item.spread != null)
setText(formatter.formatVolumeWithCode(item.spread));
setText(formatter.formatVolumeWithCode(item.spread) + item.percentage);
else
setText("-");
} else {

View file

@ -18,9 +18,12 @@
package io.bitsquare.gui.main.market.spread;
import com.google.inject.Inject;
import io.bitsquare.btc.pricefeed.MarketPrice;
import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.gui.common.model.ActivatableViewModel;
import io.bitsquare.gui.main.offer.offerbook.OfferBook;
import io.bitsquare.gui.main.offer.offerbook.OfferBookListItem;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.trade.offer.Offer;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
@ -37,6 +40,8 @@ import java.util.stream.Collectors;
class SpreadViewModel extends ActivatableViewModel {
private final OfferBook offerBook;
private PriceFeedService priceFeedService;
private BSFormatter formatter;
private final ObservableList<OfferBookListItem> offerBookListItems;
private final ListChangeListener<OfferBookListItem> listChangeListener;
final ObservableList<SpreadItem> spreadItems = FXCollections.observableArrayList();
@ -47,8 +52,10 @@ class SpreadViewModel extends ActivatableViewModel {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public SpreadViewModel(OfferBook offerBook) {
public SpreadViewModel(OfferBook offerBook, PriceFeedService priceFeedService, BSFormatter formatter) {
this.offerBook = offerBook;
this.priceFeedService = priceFeedService;
this.formatter = formatter;
offerBookListItems = offerBook.getOfferBookListItems();
listChangeListener = c -> update(offerBookListItems);
@ -108,8 +115,16 @@ class SpreadViewModel extends ActivatableViewModel {
if (bestBuyOfferPrice != null && bestSellOfferPrice != null)
spread = bestSellOfferPrice.subtract(bestBuyOfferPrice);
String percentage = "";
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
if (spread != null && marketPrice != null) {
double marketPriceAsDouble = marketPrice.getPrice(PriceFeedService.Type.LAST);
double result = ((double) spread.value / 10000) / marketPriceAsDouble;
percentage = " (" + formatter.formatPercentagePrice(result) + ")";
}
Coin totalAmount = Coin.valueOf(offers.stream().mapToLong(offer -> offer.getAmount().getValue()).sum());
spreadItems.add(new SpreadItem(currencyCode, buyOffers.size(), sellOffers.size(), offers.size(), spread, totalAmount));
spreadItems.add(new SpreadItem(currencyCode, buyOffers.size(), sellOffers.size(), offers.size(), spread, percentage, totalAmount));
}
}
}

View file

@ -19,6 +19,7 @@ package io.bitsquare.gui.main.offer.createoffer;
import com.google.inject.Inject;
import io.bitsquare.app.DevFlags;
import io.bitsquare.app.Version;
import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.FeePolicy;
@ -28,6 +29,7 @@ import io.bitsquare.btc.blockchain.BlockchainService;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.offer.createoffer.monetary.Price;
@ -137,10 +139,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
this.blockchainService = blockchainService;
this.formatter = formatter;
// isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET);
offerId = UUID.randomUUID().toString();
shortOfferId = offerId.substring(0, Math.min(8, offerId.length()));
offerId = Utilities.getRandomPrefix(5, 8) + "-" +
UUID.randomUUID().toString() + "-" +
Version.VERSION.replace(".", "");
shortOfferId = Utilities.getShortId(offerId);
addressEntry = walletService.getOrCreateAddressEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
offerFeeAsCoin = FeePolicy.getCreateOfferFee();
networkFeeAsCoin = FeePolicy.getFixedTxFeeForTrades();

View file

@ -160,7 +160,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
UserThread.runAfter(() -> {
amount.set("1");
minAmount.set(amount.get());
price.set("700");
price.set("1000");
setAmountToModel();
setMinAmountToModel();

View file

@ -147,7 +147,7 @@ public class PeerInfoWithTagEditor extends Overlay<PeerInfoWithTagEditor> {
inputTextField.setText(tag);
keyEventEventHandler = event -> {
if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event)) {
if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) {
new SendPrivateNotificationWindow(offer.getPubKeyRing(), offer.getOffererNodeAddress())
.onAddAlertMessage(privateNotificationManager::sendPrivateNotificationMessageIfKeyIsValid)
.show();

View file

@ -177,7 +177,10 @@ public class ContractWindow extends Overlay<ContractWindow> {
viewContractButton.setDefaultButton(false);
viewContractButton.setOnAction(e -> {
TextArea textArea = new TextArea();
textArea.setText(dispute.getContractAsJson());
String contractAsJson = dispute.getContractAsJson();
contractAsJson += "\n\nBuyerPubKeyHex: " + Utils.HEX.encode(dispute.getContract().getBuyerBtcPubKey());
contractAsJson += "\nSellerPubKeyHex: " + Utils.HEX.encode(dispute.getContract().getSellerBtcPubKey());
textArea.setText(contractAsJson);
textArea.setPrefHeight(50);
textArea.setEditable(false);
textArea.setWrapText(true);

View file

@ -598,7 +598,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
byte[] sellerPubKey,
byte[] arbitratorPubKey)
*/
byte[] arbitratorSignature = tradeWalletService.signDisputedPayoutTx(
byte[] arbitratorSignature = tradeWalletService.arbitratorSignsDisputedPayoutTx(
dispute.getDepositTxSerialized(),
disputeResult.getBuyerPayoutAmount(),
disputeResult.getSellerPayoutAmount(),

View file

@ -0,0 +1,207 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.main.overlays.windows;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.exceptions.TransactionVerificationException;
import io.bitsquare.btc.exceptions.WalletException;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.main.overlays.Overlay;
import io.bitsquare.gui.main.overlays.popups.Popup;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static io.bitsquare.gui.util.FormBuilder.addLabelInputTextField;
public class SpendFromDepositTxWindow extends Overlay<SpendFromDepositTxWindow> {
private static final Logger log = LoggerFactory.getLogger(SpendFromDepositTxWindow.class);
private TradeWalletService tradeWalletService;
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////////////////
public SpendFromDepositTxWindow(TradeWalletService tradeWalletService) {
this.tradeWalletService = tradeWalletService;
type = Type.Attention;
}
public void show() {
if (headLine == null)
headLine = "Emergency MS payout tool";
width = 1000;
createGridPane();
addHeadLine();
addSeparator();
addContent();
addCloseButton();
applyStyles();
display();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void setupKeyHandler(Scene scene) {
if (!hideCloseButton) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
e.consume();
doClose();
}
});
}
}
private void addContent() {
InputTextField depositTxHex = addLabelInputTextField(gridPane, ++rowIndex, "depositTxHex:").second;
InputTextField buyerPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "buyerPayoutAmount:").second;
InputTextField sellerPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "sellerPayoutAmount:").second;
InputTextField arbitratorPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPayoutAmount:").second;
InputTextField buyerAddressString = addLabelInputTextField(gridPane, ++rowIndex, "buyerAddressString:").second;
InputTextField sellerAddressString = addLabelInputTextField(gridPane, ++rowIndex, "sellerAddressString:").second;
InputTextField arbitratorAddressString = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorAddressString:").second;
InputTextField buyerPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "buyerPrivateKeyAsHex:").second;
InputTextField sellerPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "sellerPrivateKeyAsHex:").second;
InputTextField arbitratorPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPrivateKeyAsHex:").second;
InputTextField buyerPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "buyerPubKeyAsHex:").second;
InputTextField sellerPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "sellerPubKeyAsHex:").second;
InputTextField arbitratorPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPubKeyAsHex:").second;
InputTextField P2SHMultiSigOutputScript = addLabelInputTextField(gridPane, ++rowIndex, "P2SHMultiSigOutputScript:").second;
InputTextField buyerPubKeysInputTextField = addLabelInputTextField(gridPane, ++rowIndex, "buyerPubKeys:").second;
InputTextField sellerPubKeysInputTextField = addLabelInputTextField(gridPane, ++rowIndex, "sellerPubKeys:").second;
List<String> buyerPubKeys = !buyerPubKeysInputTextField.getText().isEmpty() ? Arrays.asList(buyerPubKeysInputTextField.getText().split(",")) : new ArrayList<>();
List<String> sellerPubKeys = !sellerPubKeysInputTextField.getText().isEmpty() ? Arrays.asList(sellerPubKeysInputTextField.getText().split(",")) : new ArrayList<>();
// Notes:
// Open with alt+g and enable DEV mode
// Priv key is only visible if pw protection is removed (wallet details data (alt+j))
// Take P2SHMultiSigOutputScript from depositTx in blockexplorer
// Take missing buyerPubKeyAsHex and sellerPubKeyAsHex from contract data!
// Lookup sellerPrivateKeyAsHex associated with sellerPubKeyAsHex (or buyers) in wallet details data
// sellerPubKeys/buyerPubKeys are auto generated if used the fields below
// Never set the priv arbitr. key here!
depositTxHex.setText("");
buyerPayoutAmount.setText("0.51");
sellerPayoutAmount.setText("0.01");
arbitratorPayoutAmount.setText("0");
buyerAddressString.setText("");
buyerPubKeyAsHex.setText("");
buyerPrivateKeyAsHex.setText("");
sellerAddressString.setText("");
sellerPubKeyAsHex.setText("");
sellerPrivateKeyAsHex.setText("");
//4.9
// arbitratorAddressString.setText("19xdeiQM2Hn2M2wbpT5imcYWzqhiSDHPy4");
// arbitratorPubKeyAsHex.setText("02c62e794fe67f3a2115e2de4757143ff7f27bdf38aa4ae58a3595baa6d676875b");
// 4.2
arbitratorAddressString.setText("1FdFzBazmHQxbUbdCUJwuCtR37DrZrEobu");
arbitratorPubKeyAsHex.setText("030fdc2ebc297df4047442f6079f1ce3b7d1938a41f88bd11497545cc94fcfd315");
P2SHMultiSigOutputScript.setText("");
sellerPubKeys = Arrays.asList();
buyerPubKeys = Arrays.asList();
actionButtonText("Sign and publish transaction");
final List<String> finalSellerPubKeys = sellerPubKeys;
final List<String> finalBuyerPubKeys = buyerPubKeys;
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
@Override
public void onSuccess(@Nullable Transaction result) {
log.error("onSuccess");
UserThread.execute(() -> {
String txId = result != null ? result.getHashAsString() : "null";
new Popup<>()
.information("Transaction successful published. Transaction ID: " + txId)
.show();
});
}
@Override
public void onFailure(Throwable t) {
log.error(t.toString());
log.error("onFailure");
UserThread.execute(() -> new Popup<>().warning(t.toString()).show());
}
};
onAction(() -> {
try {
tradeWalletService.emergencySignAndPublishPayoutTx(depositTxHex.getText(),
Coin.parseCoin(buyerPayoutAmount.getText()),
Coin.parseCoin(sellerPayoutAmount.getText()),
Coin.parseCoin(arbitratorPayoutAmount.getText()),
buyerAddressString.getText(),
sellerAddressString.getText(),
arbitratorAddressString.getText(),
buyerPrivateKeyAsHex.getText(),
sellerPrivateKeyAsHex.getText(),
arbitratorPrivateKeyAsHex.getText(),
buyerPubKeyAsHex.getText(),
sellerPubKeyAsHex.getText(),
arbitratorPubKeyAsHex.getText(),
P2SHMultiSigOutputScript.getText(),
finalBuyerPubKeys,
finalSellerPubKeys,
callback);
} catch (AddressFormatException | WalletException | TransactionVerificationException e) {
log.error(e.toString());
e.printStackTrace();
UserThread.execute(() -> new Popup<>().warning(e.toString()).show());
}
});
}
@Override
protected void addCloseButton() {
super.addCloseButton();
actionButton.setOnAction(event -> actionHandlerOptional.ifPresent(Runnable::run));
}
}

View file

@ -252,7 +252,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
}
public String getReference() {
return getOffer() != null ? getOffer().getReferenceText() : "";
return getOffer() != null ? getOffer().getShortId() : "";
}

View file

@ -126,7 +126,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
// we use a hidden emergency shortcut to open support ticket
keyEventEventHandler = event -> {
if (new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN).match(event)) {
if (new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN).match(event) || new KeyCodeCombination(KeyCode.O, KeyCombination.CONTROL_DOWN).match(event)) {
Popup popup = new Popup();
popup.headLine("Open support ticket")
.message("Please use that only in emergency case if you don't get displayed a \"Open support\" or \"Open dispute\" button.\n\n" +

View file

@ -206,7 +206,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
peerList.stream().forEach(e -> {
if (bitcoinPeersTextArea.getText().length() > 0)
bitcoinPeersTextArea.appendText("\n");
bitcoinPeersTextArea.appendText(e.getAddress().getSocketAddress().toString());
bitcoinPeersTextArea.appendText(e.toString());
});
}
}

View file

@ -294,10 +294,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
deviationListener = (observable, oldValue, newValue) -> {
try {
double value = formatter.parsePercentStringToDouble(newValue);
if (value <= 0.2) {
if (value <= 0.3) {
preferences.setMaxPriceDistanceInPercent(value);
} else {
new Popup().warning("Amounts larger than 20 % are not allowed.").show();
new Popup().warning("Values higher than 30 % are not allowed.").show();
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatPercentagePrice(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
}
} catch (NumberFormatException t) {

View file

@ -76,7 +76,7 @@ public class GUIUtil {
new Popup<>().information("Please be sure that the mining fee used at your external wallet is " +
"sufficiently high so that the funding transaction will be accepted by the miners.\n" +
"Otherwise the trade transactions cannot be confirmed and a trade would end up in a dispute.\n\n" +
"The recommended fee is about 50 Satoshi/Byte which is for an average transaction about 0.0002 BTC.\n\n" +
"The recommended fee is about 120 Satoshi/Byte which is for an average transaction about 0.0005 BTC.\n\n" +
"You can view typically used fees at: https://tradeblock.com/blockchain")
.dontShowAgainId(key, Preferences.INSTANCE)
.onClose(runnable::run)
@ -87,7 +87,6 @@ public class GUIUtil {
}
}
public static void exportAccounts(ArrayList<PaymentAccount> accounts, String fileName, Preferences preferences, Stage stage) {
if (!accounts.isEmpty()) {
String directory = getDirectoryFormChooser(preferences, stage);

View file

@ -20,6 +20,7 @@ package io.bitsquare.gui.util.validation;
import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.BankUtil;
import org.apache.commons.lang3.StringUtils;
public final class AccountNrValidator extends BankValidator {
public AccountNrValidator(String countryCode) {
@ -75,12 +76,58 @@ public final class AccountNrValidator extends BankValidator {
return super.validate(input);
else
return new ValidationResult(false, "Account number must be of format: 005-231289-112");
case "NO":
if (input != null) {
length = 11;
// Provided by sturles:
// https://github.com/bitsquare/bitsquare/pull/707
// https://no.wikipedia.org/wiki/MOD11#Implementasjoner_i_forskjellige_programmeringspr.C3.A5k
// https://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits6
// 11 digits, last digit is checksum. Checksum algoritm is
// MOD11 with weights 2,3,4,5,6,7,2,3,4,5 right to left.
// First remove whitespace and periods. Normal formatting is:
// 1234.56.78903
input2 = StringUtils.remove(input, " ");
input2 = StringUtils.remove(input2, ".");
// 11 digits, numbers only
if (input2.length() != length || !StringUtils.isNumeric(input2))
return new ValidationResult(false, BSResources.get("validation.sortCodeNumber", getLabel(), length));
int lastDigit = Character.getNumericValue(input2.charAt(input2.length() - 1));
if (getMod11ControlDigit(input2) != lastDigit)
return new ValidationResult(false, "Kontonummer har feil sjekksum");
else
return super.validate(input);
} else {
return super.validate(input);
}
default:
return super.validate(input);
}
}
private int getMod11ControlDigit(String accountNrString) {
int sumForMod = 0;
int controlNumber = 2;
char[] accountNr = accountNrString.toCharArray();
for (int i = accountNr.length - 2; i >= 0; i--) {
sumForMod += (Character.getNumericValue(accountNr[i]) * controlNumber);
controlNumber++;
if (controlNumber > 7) {
controlNumber = 2;
}
}
int calculus = (11 - sumForMod % 11);
if (calculus == 11) {
return 0;
} else {
return calculus;
}
}
private String getLabel() {
String label = BankUtil.getAccountNrLabel(countryCode);

View file

@ -18,16 +18,129 @@
package io.bitsquare.gui.util.validation;
import io.bitsquare.gui.util.validation.altcoins.ByteballAddressValidator;
import io.bitsquare.gui.util.validation.params.IOPParams;
import io.bitsquare.gui.util.validation.params.PivxParams;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.params.MainNetParams;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class AltCoinAddressValidator extends InputValidator {
private static final Logger log = LoggerFactory.getLogger(AltCoinAddressValidator.class);
private String currencyCode;
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public void setCurrencyCode(String currencyCode) {
this.currencyCode = currencyCode;
}
@Override
public ValidationResult validate(String input) {
ValidationResult validationResult = super.validate(input);
if (!validationResult.isValid || currencyCode == null) {
return validationResult;
} else {
// Validation:
// 1: With a regex checking the correct structure of an address
// 2: If the address contains a checksum, verify the checksum
ValidationResult wrongChecksum = new ValidationResult(false, "Address validation failed because checksum was not correct.");
ValidationResult regexTestFailed = new ValidationResult(false, "Address validation failed because it does not match the structure of a " + currencyCode + " address.");
switch (currencyCode) {
// Example for BTC, though for BTC we use the BitcoinJ library address check
case "BTC":
// taken form: https://stackoverflow.com/questions/21683680/regex-to-match-bitcoin-addresses
if (input.matches("^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$")) {
if (verifyChecksum(input))
try {
new Address(MainNetParams.get(), input);
return new ValidationResult(true);
} catch (AddressFormatException e) {
return new ValidationResult(false, getErrorMessage(e));
}
else
return wrongChecksum;
} else {
return regexTestFailed;
}
case "PIVX":
if (input.matches("^[D][a-km-zA-HJ-NP-Z1-9]{25,34}$")) {
if (verifyChecksum(input)) {
try {
new Address(PivxParams.get(), input);
return new ValidationResult(true);
} catch (AddressFormatException e) {
return new ValidationResult(false, getErrorMessage(e));
}
} else {
return wrongChecksum;
}
} else {
return regexTestFailed;
}
case "IOP":
if (input.matches("^[p][a-km-zA-HJ-NP-Z1-9]{25,34}$")) {
if (verifyChecksum(input)) {
try {
new Address(IOPParams.get(), input);
return new ValidationResult(true);
} catch (AddressFormatException e) {
return new ValidationResult(false, getErrorMessage(e));
}
} else {
return wrongChecksum;
}
} else {
return regexTestFailed;
}
case "ZEC":
// We only support t addresses (transparent transactions)
if (input.startsWith("t"))
return validationResult;
else
return new ValidationResult(false, "ZEC address need to start with t. Addresses starting with z are not supported.");
// TODO test not successful
/*case "XTO":
if (input.matches("^[T2][a-km-zA-HJ-NP-Z1-9]{25,34}$")) {
if (verifyChecksum(input))
try {
new Address(MainNetParams.get(), input);
return new ValidationResult(true);
} catch (AddressFormatException e) {
return new ValidationResult(false, getErrorMessage(e));
}
else
return wrongChecksum;
} else {
return regexTestFailed;
}*/
case "GBYTE":
return ByteballAddressValidator.validate(input);
default:
log.debug("Validation for AltCoinAddress not implemented yet. currencyCode:" + currencyCode);
return validationResult;
}
}
}
@NotNull
private String getErrorMessage(AddressFormatException e) {
return "Address is not a valid " + currencyCode + " address! " + e.getMessage();
}
private boolean verifyChecksum(String input) {
// TODO
return super.validate(input);
return true;
}

View file

@ -17,6 +17,24 @@
package io.bitsquare.gui.util.validation;
import java.util.Locale;
/*
* BIC information taken from German wikipedia (2017-01-30)
*
* length 8 or 11 characters
* General format: BBBB CC LL (bbb)
* with B - Bank code
* C - Country code
* L - Location code
* b - branch code (if applicable)
*
* B and C must be letters
* first L cannot be 0 or 1, second L cannot be O (upper case 'o')
* bbb cannot begin with X, unless it is XXX
*/
// TODO Special letters like ä, å, ... are not detected as invalid
public final class BICValidator extends InputValidator {
@ -29,8 +47,34 @@ public final class BICValidator extends InputValidator {
// TODO Add validation for primary and secondary IDs according to the selected type
// IBAN max 34 chars
// bic: max 11 char
return super.validate(input);
// bic: 8 or 11 chars
// check ensure length 8 or 11
if (!isStringWithFixedLength(input,8) && !isStringWithFixedLength(input,11))
return new ValidationResult(false, "Input length is neither 8 nor 11");
input = input.toUpperCase(Locale.ROOT);
// ensure Bank and Contry code to be letters only
for (int k=0; k<6; k++) {
if (!Character.isLetter(input.charAt(k)))
return new ValidationResult(false, "Bank and Country code must be letters");
}
// ensure location code starts not with 0 or 1 and ends not with O
char ch = input.charAt(6);
if (ch == '0' || ch == '1' || input.charAt(7) == 'O')
return new ValidationResult(false, "BIC contains invalid location code");
// check complete for 8 char BIC
if (input.length() == 8)
return new ValidationResult(true);
// ensure branch code does not start with X unless it is XXX
if (input.charAt(8) == 'X')
if (input.charAt(9) != 'X' || input.charAt(10) != 'X')
return new ValidationResult(false, "BIC contains invalid branch code");
return new ValidationResult(true);
}

View file

@ -17,23 +17,22 @@
package io.bitsquare.gui.util.validation;
public final class ChaseQuickPayValidator extends InputValidator {
private final EmailValidator emailValidator;
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public ValidationResult validate(String input) {
// TODO
return super.validate(input);
public ChaseQuickPayValidator() {
super();
emailValidator = new EmailValidator();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public ValidationResult validate(String input) {
return emailValidator.validate(input);
}
}

View file

@ -17,6 +17,22 @@
package io.bitsquare.gui.util.validation;
/*
* Mail addresses consist of localPart @ domainPart
*
* Local part:
* May contain lots of symbols A-Za-z0-9.!#$%&'*+-/=?^_`{|}~
* but cannot begin or end with a dot (.)
* between double quotes many more symbols are allowed:
* "(),:;<>@[\] (ASCII: 32, 34, 40, 41, 44, 58, 59, 60, 62, 64, 9193)
*
* Domain part:
* Consists of name dot TLD
* name can but usually doesn't (compatibility reasons) contain non-ASCII
* symbols.
* TLD is at least two letters long
*
*/
public final class EmailValidator extends InputValidator {
@ -24,10 +40,49 @@ public final class EmailValidator extends InputValidator {
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
private final ValidationResult invalidAddress = new ValidationResult(false, "Invalid address");
@Override
public ValidationResult validate(String input) {
// TODO
return super.validate(input);
if (input == null || input.length() < 6) // shortest address is l@d.cc
return invalidAddress;
String[] subStrings;
String local, domain;
subStrings = input.split("@", -1);
if (subStrings.length == 1) // address does not contain '@'
return invalidAddress;
if (subStrings.length > 2) // multiple @'s included -> check for valid double quotes
if (!checkForValidQuotes(subStrings)) // around @'s -> "..@..@.." and concatenate local part
return invalidAddress;
local = subStrings[0];
domain = subStrings[subStrings.length - 1];
if (local.isEmpty())
return invalidAddress;
// local part cannot begin or end with '.'
if (local.startsWith(".") || local.endsWith("."))
return invalidAddress;
String[] splitDomain = domain.split("\\.", -1); // '.' is a regex in java and has to be escaped
String tld = splitDomain[splitDomain.length - 1];
if (splitDomain.length < 2)
return invalidAddress;
if (splitDomain[0] == null || splitDomain[0].isEmpty())
return invalidAddress;
// TLD length is at least two
if (tld.length() < 2)
return invalidAddress;
// TLD is letters only
for (int k = 0; k < tld.length(); k++)
if (!Character.isLetter(tld.charAt(k)))
return invalidAddress;
return new ValidationResult(true);
}
@ -35,5 +90,22 @@ public final class EmailValidator extends InputValidator {
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private boolean checkForValidQuotes(String[] subStrings) {
int length = subStrings.length - 2; // is index on last substring of local part
// check for odd number of double quotes before first and after last '@'
if ((subStrings[0].split("\"", -1).length % 2 == 1) || (subStrings[length].split("\"", -1).length % 2 == 1))
return false;
for (int k = 1; k < length; k++) {
if (subStrings[k].split("\"", -1).length % 2 == 0)
return false;
}
String patchLocal = "";
for (int k = 0; k <= length; k++) // remember: length is last index not array length
patchLocal = patchLocal.concat(subStrings[k]); // @'s are not reinstalled, since not needed for further checks
subStrings[0] = patchLocal;
return true;
}
}

View file

@ -17,6 +17,11 @@
package io.bitsquare.gui.util.validation;
import java.math.BigInteger;
import java.util.Locale;
// TODO Does not yet recognize special letters like ä, ö, ü, å, ... as invalid characters
public final class IBANValidator extends InputValidator {
@ -28,9 +33,56 @@ public final class IBANValidator extends InputValidator {
public ValidationResult validate(String input) {
// TODO Add validation for primary and secondary IDs according to the selected type
// IBAN max 34 chars
// IBAN max 34 chars, shortest is Norwegian with 15 chars, BBAN may include letters
// bic: max 11 char
return super.validate(input);
// check input length first
if (isStringInRange(input, 15, 34)) {
input = input.toUpperCase(Locale.ROOT); // ensure upper case
// check if country code is letters and checksum numeric
if (!( Character.isLetter(input.charAt(0)) && Character.isLetter(input.charAt(1)) ))
return new ValidationResult(false, "Country code invalid");
if (!( Character.isDigit(input.charAt(2)) && Character.isDigit(input.charAt(3)) ))
return new ValidationResult(false, "Checksum must be numeric");
// reorder IBAN to format <account number> <country code> <checksum>
String input2 = new String(input.substring(4, input.length()) + input.substring(0,4));
// check if input is alphanumeric and count included letters
int charCount = 0;
char ch;
for (int k=0; k<input2.length(); k++) {
ch = input2.charAt(k);
if (Character.isLetter(ch))
charCount++;
else if (!Character.isDigit(ch))
return (new ValidationResult(false, "Non-alphanumeric character detected"));
}
// create final char array for checksum validation
char [] charArray = new char[input2.length()+charCount];
int i = 0;
int tmp;
for (int k=0; k<input2.length(); k++) {
ch = input2.charAt(k);
if (Character.isLetter(ch)) {
tmp = ch - ('A' - 10); // letters are transformed to two digit numbers A->10, B->11, ...
String s = Integer.toString(tmp);
charArray[i++] = s.charAt(0); // insert transformed
charArray[i++] = s.charAt(1); // letters into char array
} else charArray[i++] = ch; // transfer digits directly to char array
}
// System.out.print(Arrays.toString(charArray) + '\t');
BigInteger bigInt = new BigInteger(new String(charArray));
int result = bigInt.mod(new BigInteger(Integer.toString(97))).intValue();
if (result == 1)
return new ValidationResult(true);
else
return new ValidationResult(false, "IBAN checksum is invalid");
}
// return new ValidationResult(false, BSResources.get("validation.accountNrChars", "15 - 34"));
return new ValidationResult(false, "Number must have length 15 to 34 chars.");
}

View file

@ -17,17 +17,44 @@
package io.bitsquare.gui.util.validation;
/*
* Interac e-Transfer requires a mail address or Canadian (mobile) phone number
*
* Mail addresses are covered with class EmailValidator
*
* Phone numbers have 11 digits, expected format is +1 NPA xxx-xxxx
* Plus, spaces and dash might be omitted
* Canadian area codes (NPA) taken from http://www.cnac.ca/canadian_dial_plan/Current_&_Future_Dialling_Plan.pdf
* Valid (as of 2017-06-27) NPAs are hardcoded here
* They are to change in some future (according to the linked document around 2019/2020)
*/
public final class InteracETransferValidator extends InputValidator {
private static final String[] NPAS = {"204", "226", "236", "249", "250", "289", "306", "343", "365", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "705", "709", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"};
private final EmailValidator emailValidator;
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public InteracETransferValidator() {
emailValidator = new EmailValidator();
}
@Override
public ValidationResult validate(String input) {
// TODO
return super.validate(input);
ValidationResult result = validateIfNotEmpty(input);
if (!result.isValid) {
return result;
} else {
ValidationResult emailResult = emailValidator.validate(input);
if (emailResult.isValid)
return emailResult;
else
return validatePhoneNumber(input);
}
}
@ -35,5 +62,22 @@ public final class InteracETransferValidator extends InputValidator {
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private ValidationResult validatePhoneNumber(String input) {
// check for correct format and strip +, space and -
if (input.matches("\\+?1[ -]?\\d{3}[ -]?\\d{3}[ -]?\\d{4}")) {
input = input.replace("+", "");
input = input.replace(" ", "");
input = input.replace("-", "");
String inputAreaCode = input.substring(1, 4);
for (String s : NPAS) {
// check area code agains list and return if valid
if (inputAreaCode.compareTo(s) == 0)
return new ValidationResult(true);
}
return new ValidationResult(false, "Non-Canadian area code");
} else {
return new ValidationResult(false, "Invalid phone number format and not an email address");
}
}
}

View file

@ -0,0 +1,178 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.util.validation.altcoins;
import io.bitsquare.gui.util.validation.InputValidator;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by DevAlexey on 19.12.2016.
*/
public class ByteballAddressValidator {
private static final Base32 base32 = new Base32();
private static final Base64 base64 = new Base64();
private static final String PI = "14159265358979323846264338327950288419716939937510";
private static final String[] arrRelativeOffsets = PI.split("");
private static Integer[] arrOffsets160, arrOffsets288;
static {
try {
arrOffsets160 = calcOffsets(160);
} catch (Exception e) {
e.printStackTrace();
}
try {
arrOffsets288 = calcOffsets(288);
} catch (Exception e) {
e.printStackTrace();
}
}
public static InputValidator.ValidationResult validate(String input) {
return new InputValidator.ValidationResult(isValidAddress(input));
}
private static boolean isValidAddress(String address) {
return isValidChash(address, 32);
}
private static boolean isValidChash(String str, int len) {
return (isStringOfLength(str, len) && isChashValid(str));
}
private static boolean isStringOfLength(String str, int len) {
return str.length() == len;
}
private static void checkLength(int chash_length) throws Exception {
if (chash_length != 160 && chash_length != 288)
throw new Exception("unsupported c-hash length: " + chash_length);
}
private static Integer[] calcOffsets(int chash_length) throws Exception {
checkLength(chash_length);
List<Integer> arrOffsets = new ArrayList<>(chash_length);
int offset = 0;
int index = 0;
for (int i = 0; offset < chash_length; i++) {
int relative_offset = Integer.parseInt(arrRelativeOffsets[i]);
if (relative_offset == 0)
continue;
offset += relative_offset;
if (chash_length == 288)
offset += 4;
if (offset >= chash_length)
break;
arrOffsets.add(offset);
//console.log("index="+index+", offset="+offset);
index++;
}
if (index != 32)
throw new Exception("wrong number of checksum bits");
return arrOffsets.toArray(new Integer[0]);
}
private static SeparatedData separateIntoCleanDataAndChecksum(String bin) throws Exception {
int len = bin.length();
Integer[] arrOffsets;
if (len == 160)
arrOffsets = arrOffsets160;
else if (len == 288)
arrOffsets = arrOffsets288;
else
throw new Exception("bad length");
StringBuilder arrFrags = new StringBuilder();
StringBuilder arrChecksumBits = new StringBuilder();
int start = 0;
for (int i = 0; i < arrOffsets.length; i++) {
arrFrags.append(bin.substring(start, arrOffsets[i]));
arrChecksumBits.append(bin.substring(arrOffsets[i], arrOffsets[i] + 1));
start = arrOffsets[i] + 1;
}
// add last frag
if (start < bin.length())
arrFrags.append(bin.substring(start));
String binCleanData = arrFrags.toString();
String binChecksum = arrChecksumBits.toString();
return new SeparatedData(binCleanData, binChecksum);
}
private static String buffer2bin(byte[] buf) {
StringBuffer bytes = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String bin = String.format("%8s", Integer.toBinaryString(buf[i] & 0xFF)).replace(' ', '0');
bytes.append(bin);
}
return bytes.toString();
}
private static byte[] bin2buffer(String bin) {
int len = bin.length() / 8;
byte[] buf = new byte[len];
for (int i = 0; i < len; i++)
buf[i] = (byte) Integer.parseInt(bin.substring(i * 8, (i + 1) * 8), 2);
return buf;
}
private static boolean isChashValid(String encoded) {
int encoded_len = encoded.length();
if (encoded_len != 32 && encoded_len != 48) // 160/5 = 32, 288/6 = 48
return false;
byte[] chash = (encoded_len == 32) ? base32.decode(encoded) : base64.decode(encoded);
String binChash = buffer2bin(chash);
SeparatedData separated = null;
try {
separated = separateIntoCleanDataAndChecksum(binChash);
} catch (Exception e) {
return false;
}
byte[] clean_data = bin2buffer(separated.clean_data);
byte[] checksum = bin2buffer(separated.checksum);
return Arrays.equals(getChecksum(clean_data), checksum);
}
private static byte[] getChecksum(byte[] clean_data) {
try {
byte[] full_checksum = MessageDigest.getInstance("SHA-256").digest(clean_data);
byte[] checksum = {full_checksum[5], full_checksum[13], full_checksum[21], full_checksum[29]};
return checksum;
} catch (NoSuchAlgorithmException e) {
return null;
}
}
private static class SeparatedData {
String clean_data, checksum;
public SeparatedData(String clean_data, String checksum) {
this.clean_data = clean_data;
this.checksum = checksum;
}
}
}

View file

@ -0,0 +1,78 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.util.validation.params;
import org.bitcoinj.core.*;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.utils.MonetaryFormat;
public class IOPParams extends NetworkParameters {
private static IOPParams instance;
public static synchronized IOPParams get() {
if (instance == null) {
instance = new IOPParams();
}
return instance;
}
// We only use the properties needed for address validation
public IOPParams() {
super();
addressHeader = 117;
p2shHeader = 174;
acceptableAddressCodes = new int[]{addressHeader, p2shHeader};
}
// default dummy implementations, not used...
@Override
public String getPaymentProtocolId() {
return PAYMENT_PROTOCOL_ID_MAINNET;
}
@Override
public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException {
}
@Override
public Coin getMaxMoney() {
return null;
}
@Override
public Coin getMinNonDustOutput() {
return null;
}
@Override
public MonetaryFormat getMonetaryFormat() {
return null;
}
@Override
public String getUriScheme() {
return null;
}
@Override
public boolean hasMaxMoney() {
return false;
}
}

View file

@ -0,0 +1,78 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.gui.util.validation.params;
import org.bitcoinj.core.*;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.utils.MonetaryFormat;
public class PivxParams extends NetworkParameters {
private static PivxParams instance;
public static synchronized PivxParams get() {
if (instance == null) {
instance = new PivxParams();
}
return instance;
}
// We only use the properties needed for address validation
public PivxParams() {
super();
addressHeader = 30;
p2shHeader = 13;
acceptableAddressCodes = new int[]{addressHeader, p2shHeader};
}
// default dummy implementations, not used...
@Override
public String getPaymentProtocolId() {
return PAYMENT_PROTOCOL_ID_MAINNET;
}
@Override
public void checkDifficultyTransitions(StoredBlock storedPrev, Block next, BlockStore blockStore) throws VerificationException, BlockStoreException {
}
@Override
public Coin getMaxMoney() {
return null;
}
@Override
public Coin getMinNonDustOutput() {
return null;
}
@Override
public MonetaryFormat getMonetaryFormat() {
return null;
}
@Override
public String getUriScheme() {
return null;
}
@Override
public boolean hasMaxMoney() {
return false;
}
}

View file

@ -217,7 +217,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -230,12 +230,11 @@ FASTER_PAYMENTS_SHORT=Faster Payments
NATIONAL_BANK_SHORT=National banks
SAME_BANK_SHORT=Same bank
SPECIFIC_BANKS_SHORT=Specific banks
FED_WIRE_SHORT=Fed Wire
SWISH_SHORT=Swish
CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,7 +238,7 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

View file

@ -219,7 +219,7 @@ CLEAR_X_CHANGE=ClearXchange
CHASE_QUICK_PAY=Chase QuickPay
INTERAC_E_TRANSFER=Interac e-Transfer
US_POSTAL_MONEY_ORDER=US Postal Money Order
CASH_DEPOSIT=Cash/ATM deposit
CASH_DEPOSIT=Cash Deposit
BLOCK_CHAINS=Altcoins
@ -238,6 +238,6 @@ CLEAR_X_CHANGE_SHORT=ClearXchange
CHASE_QUICK_PAY_SHORT=Chase QuickPay
INTERAC_E_TRANSFER_SHORT=Interac e-Transfer
US_POSTAL_MONEY_ORDER_SHORT=US Money Order
CASH_DEPOSIT_SHORT=Cash/ATM deposit
CASH_DEPOSIT_SHORT=Cash Deposit
BLOCK_CHAINS_SHORT=Altcoins

Some files were not shown because too many files have changed in this diff Show more