payment to MS fund

This commit is contained in:
Manfred Karrer 2014-05-07 13:32:05 +02:00
parent 7d0676a995
commit a4efa29bcd
37 changed files with 1081 additions and 546 deletions

View file

@ -19,11 +19,13 @@ git clone --recursive git://github.com/bitsquare/bitsquare
* bitcoinj integration
* Setup with account registration and tx with OP_RETURN + embedded and blinded bank account data
* Offer fee payment with a OP_RETURN tx and fees to miners
* Pay in to MS fund
### Next steps:
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
* Payout from MS fund
* Arbitrator integration
* Messaging system
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
* ...

View file

@ -1,5 +1,4 @@
- payment process update with new models
- btc payments in payment process
- pay out from MS in payment process
- arbitration integration
Messaging!
@ -9,8 +8,7 @@ low prio:
- add settings after setup
- settings screen
- return to setup when unregistered, change/add bank accounts from settings
- warning popups
-
- BigInteger for all btc values

View file

@ -1,5 +1,6 @@
package io.bitsquare;
import com.google.bitcoin.core.Utils;
import com.google.inject.Guice;
import com.google.inject.Injector;
import io.bitsquare.bank.BankAccount;
@ -45,9 +46,11 @@ public class BitSquare extends Application
final Storage storage = injector.getInstance(Storage.class);
user.updateFromStorage((User) storage.read(user.getClass().getName()));
settings.updateFromStorage((Settings) storage.read(settings.getClass().getName()));
// mock
initSettings(settings, storage, user);
settings.updateFromStorage((Settings) storage.read(settings.getClass().getName()));
stage.setTitle("BitSquare");
GuiceFXMLLoader.setInjector(injector);
@ -83,55 +86,62 @@ public class BitSquare extends Application
{
// write default settings
settings.getAcceptedCountryLocales().clear();
settings.getAcceptedLanguageLocales().clear();
settings.addAcceptedLanguageLocale(Locale.getDefault());
settings.addAcceptedCountryLocale(Locale.getDefault());
//TODO mock
// settings.addAcceptedLanguageLocale(Locale.getDefault());
settings.addAcceptedLanguageLocale(MockData.getLocales().get(0));
settings.addAcceptedLanguageLocale(new Locale("en", "US"));
settings.addAcceptedLanguageLocale(new Locale("es", "ES"));
settings.addAcceptedCountryLocale(new Locale("de", "AT"));
settings.getAcceptedCountryLocales().clear();
//settings.addAcceptedCountryLocale(Locale.getDefault());
settings.addAcceptedCountryLocale(MockData.getLocales().get(0));
settings.addAcceptedCountryLocale(new Locale("en", "US"));
settings.addAcceptedCountryLocale(new Locale("es", "ES"));
settings.addArbitrator(new Arbitrator("Charly Boom", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom"));
settings.addArbitrator(new Arbitrator("Tom Shang", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang"));
settings.addArbitrator(new Arbitrator("Edward Snow", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow"));
settings.addArbitrator(new Arbitrator("Julian Sander", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander"));
settings.getAcceptedArbitrators().clear();
settings.addAcceptedArbitrator(new Arbitrator("uid_1", "Charlie Boom", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom", 0.1, 10, Utils.toNanoCoins("0.01")));
settings.addAcceptedArbitrator(new Arbitrator("uid_2", "Tom Shang", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang", 0, 1, Utils.toNanoCoins("0.001")));
settings.addAcceptedArbitrator(new Arbitrator("uid_3", "Edward Snow", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow", 0.2, 5, Utils.toNanoCoins("0.05")));
settings.addAcceptedArbitrator(new Arbitrator("uid_4", "Julian Sander", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander", 0, 20, Utils.toNanoCoins("0.1")));
settings.setMinCollateral(0.01);
settings.setMaxCollateral(0.1);
storage.write(settings.getClass().getName(), settings);
initMockUser(storage, user);
}
else
{
settings.updateFromStorage(savedSettings);
//initMockUser(storage, user);
}
}
private void initMockUser(Storage storage, User user)
{
user.getBankAccounts().clear();
BankAccount bankAccount1 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.SEPA, "Iban", "Bic"),
MockData.getCurrencies().get(0),
MockData.getLocales().get(0),
"Main account",
"Main EUR account",
"Manfred Karrer",
"564613242346",
"23432432434"
);
BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.OK_PAY, "Number", "ID"),
MockData.getCurrencies().get(0),
MockData.getLocales().get(0),
"OK account",
user.addBankAccount(bankAccount1);
BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.INTERNATIONAL, "Number", "ID"),
MockData.getCurrencies().get(1),
MockData.getLocales().get(2),
"US account",
"Manfred Karrer",
"22312123123123123",
"asdasdasdas"
);
user.addBankAccount(bankAccount2);
user.addBankAccount(bankAccount1);
user.setAccountID(UUID.randomUUID().toString());
storage.write(user.getClass().getName(), user);
}
}

View file

@ -42,20 +42,23 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
walletFile = new File(".", "bitsquare_account_reg" + ".wallet");
if (walletFile.exists())
{
FileInputStream walletStream = null;
try
{
FileInputStream walletStream = new FileInputStream(walletFile);
new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this);
walletStream = new FileInputStream(walletFile);
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (UnreadableWalletException e)
}
try
{
e.printStackTrace();
} catch (IOException e)
new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this);
} catch (UnreadableWalletException | IOException e)
{
e.printStackTrace();
}
}
else
{
@ -69,7 +72,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
saveToFile(walletFile);
} catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
e.printStackTrace();
}
autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null);
}
@ -81,7 +84,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
saveToFile(walletFile);
} catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
e.printStackTrace();
}
}

View file

@ -1,6 +1,12 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.Utils;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Locale;
public class BtcFormatter
{
@ -12,4 +18,31 @@ public class BtcFormatter
{
return satoshis.doubleValue() / BTC.doubleValue();
}
public static BigInteger stringValueToSatoshis(String value)
{
return Utils.toNanoCoins(String.valueOf(Converter.stringToDouble(value)));
}
public static BigInteger doubleValueToSatoshis(double value)
{
try
{
// only "." as decimal sep supported by Utils.toNanoCoins
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
String stringValue = decimalFormat.format(value);
return Utils.toNanoCoins(stringValue);
} catch (Exception e)
{
return BigInteger.ZERO;
}
}
public static String formatSatoshis(BigInteger satoshis, boolean useBTC)
{
if (useBTC)
return Formatter.formatDouble(satoshiToBTC(satoshis), 8) + " BTC";
else
return Formatter.formatDouble(satoshiToBTC(satoshis), 8);
}
}

View file

@ -1,13 +1,11 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.Transaction;
import java.math.BigInteger;
public class Fees
{
public static BigInteger ACCOUNT_REGISTRATION_FEE = Transaction.MIN_NONDUST_OUTPUT;// Utils.toNanoCoins("0.001");
public static BigInteger OFFER_CREATION_FEE = Transaction.MIN_NONDUST_OUTPUT; // Utils.toNanoCoins("0.001");
public static BigInteger OFFER_TAKER_FEE = OFFER_CREATION_FEE;
// min dust value lead to exception at for non standard to address pay scripts, so we use a value >= 7860 instead
public static BigInteger ACCOUNT_REGISTRATION_FEE = BigInteger.valueOf(7860);// Utils.toNanoCoins("0.001");
public static BigInteger OFFER_CREATION_FEE = BigInteger.valueOf(7860); // //Transaction.MIN_NONDUST_OUTPUT; // Utils.toNanoCoins("0.001");
public static BigInteger OFFER_TAKER_FEE = BigInteger.valueOf(7860);
}

View file

@ -1,16 +1,20 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.*;
import com.google.bitcoin.crypto.TransactionSignature;
import com.google.bitcoin.kits.WalletAppKit;
import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.RegTestParams;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.utils.Threading;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.gui.util.Popups;
import javafx.application.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -19,9 +23,7 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
import java.util.concurrent.ExecutionException;
/**
* That facade delivers wallet functionality from the bitcoinJ library
@ -33,28 +35,44 @@ public class WalletFacade implements WalletEventListener
public static final String MAIN_NET = "MAIN_NET";
public static final String TEST_NET = "TEST_NET";
// for testing trade process between offerer and taker
//public static final String WALLET_PREFIX = "offerer"; // offerer
// public static final String WALLET_PREFIX = "taker"; // offerer
public static final String WALLET_PREFIX = "bitsquare";
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
private NetworkParameters networkParameters;
private NetworkParameters params;
private WalletAppKit walletAppKit;
private CryptoFacade cryptoFacade;
private BlockChainFacade blockChainFacade;
// that wallet is used only for the registration process
private AccountRegistrationWallet accountRegistrationWallet = null;
private List<DownloadListener> downloadListeners = new ArrayList<>();
private List<WalletListener> walletListeners = new ArrayList<>();
private Wallet wallet;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public WalletFacade(NetworkParameters networkParameters, WalletAppKit walletAppKit, CryptoFacade cryptoFacade, BlockChainFacade blockChainFacade)
public WalletFacade(NetworkParameters params, WalletAppKit walletAppKit, CryptoFacade cryptoFacade)
{
this.networkParameters = networkParameters;
this.params = params;
this.walletAppKit = walletAppKit;
this.cryptoFacade = cryptoFacade;
this.blockChainFacade = blockChainFacade;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void initWallet()
{
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means
@ -63,11 +81,11 @@ public class WalletFacade implements WalletEventListener
// a future version.
Threading.USER_THREAD = Platform::runLater;
if (networkParameters == RegTestParams.get())
if (params == RegTestParams.get())
{
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally.
}
else if (networkParameters == MainNetParams.get())
else if (params == MainNetParams.get())
{
// Checkpoints are block headers that ship inside our app: for a new user, we pick the last header
// in the checkpoints file and then download the rest from the network. It makes things much faster.
@ -80,16 +98,214 @@ public class WalletFacade implements WalletEventListener
// or progress widget to keep the user engaged whilst we initialise, but we don't.
walletAppKit.setDownloadListener(new BlockChainDownloadListener())
.setBlockingStartup(false)
.setUserAgent("BitSquare", "1.0");
.setUserAgent("BitSquare", "0.1");
walletAppKit.startAsync();
walletAppKit.awaitRunning();
wallet = walletAppKit.wallet();
// Don't make the user wait for confirmations for now, as the intention is they're sending it their own money!
getWallet().allowSpendingUnconfirmedTransactions();
walletAppKit.peerGroup().setMaxConnections(11);
wallet.allowSpendingUnconfirmedTransactions();
walletAppKit.peerGroup().setMaxConnections(20);
getWallet().addEventListener(this);
wallet.addEventListener(this);
log.info(getWallet().toString());
log.info(wallet.toString());
// testTradeProcess();
}
// TODO only temp. for testing trade process between offerer and taker
private void testTradeProcess()
{
//wallet.allowSpendingUnconfirmedTransactions();
try
{
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
BigInteger offererAmount = Utils.toNanoCoins("0.01");
BigInteger takerAmount = Utils.toNanoCoins("0.02");
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7";
String arbitratorPubKey = "";
// 1 offerer creates MS TX and pay in
/* Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize());
*/
tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000";
// 2. taker pay in and sign
/* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex);
tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize());
tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes());
tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
*/
tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
// 3. offerer sign and send
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex);
log.info(tx3.toString());
} catch (AddressFormatException | InsufficientMoneyException | InterruptedException | ExecutionException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (Exception e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
// 1. offerer
private Transaction offererCreatesMSTxAndAddPayment(BigInteger offererAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
{
// use that to use the convenient api for getting the best coin selection and fee calculation
// TODO should be constructed manually
Transaction tx = new Transaction(params);
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
tx.addOutput(offererAmount, multiSigOutputScript);
Wallet.SendRequest request = Wallet.SendRequest.forTx(tx);
wallet.completeTx(request);
// TODO remove sig or use SigHash.NONE
//tx.getInput(0).setScriptSig(null);
/*
IN[0] offerer
OUT[0] MS
OUT[1] offerer change
*/
return tx;
}
// 2. taker
public Transaction takerAddPaymentAndSign(BigInteger takerAmount,
BigInteger msOutputAmount,
String offererPubKey,
String takerPubKey,
String arbitratorPubKey,
String tx1AsHex
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException
{
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
// use that to use the convenient api for getting the best coin selection and fee calculation
// TODO should be constructed manually
Transaction dummyTx = new Transaction(params);
dummyTx.addOutput(takerAmount, multiSigOutputScript);
wallet.completeTx(Wallet.SendRequest.forTx(dummyTx));
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
tx.addInput(dummyTx.getInput(0));
tx.addOutput(dummyTx.getOutput(1));
tx.getOutput(0).setValue(msOutputAmount);
TransactionInput input = tx.getInput(1);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
Sha256Hash hash = tx.hashForSignature(1, scriptPubKey, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature ecSig = sigKey.sign(hash);
TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false);
if (scriptPubKey.isSentToRawPubKey())
input.setScriptSig(ScriptBuilder.createInputScript(txSig));
else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
/*
IN[0] offerer
IN[1] taker signed
OUT[0] MS
OUT[1] offerer change
OUT[2] taker change
*/
return tx;
}
public Transaction offererSignAndSendTx(String tx1AsHex,
String tx2AsHex,
String tx2ConnOutAsHex,
String tx2ScriptSigAsHex) throws Exception
{
Transaction tx = new Transaction(params);
Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash());
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 0, tx1ConnOut);
TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint);
tx1Input.setParent(tx);
tx.addInput(tx1Input);
Transaction tx2 = new Transaction(params, Utils.parseAsHexOrBase58(tx2AsHex));
Transaction tx2ConnOut = new Transaction(params, Utils.parseAsHexOrBase58(tx2ConnOutAsHex));
TransactionOutPoint tx2OutPoint = new TransactionOutPoint(params, 1, tx2ConnOut);
TransactionInput tx2Input = new TransactionInput(params, tx, Utils.parseAsHexOrBase58(tx2ScriptSigAsHex), tx2OutPoint);
tx2Input.setParent(tx);
tx.addInput(tx2Input);
tx.addOutput(tx2.getOutput(0));
tx.addOutput(tx2.getOutput(1));
tx.addOutput(tx2.getOutput(2));
TransactionInput input = tx.getInput(0);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
Sha256Hash hash = tx.hashForSignature(0, scriptPubKey, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature ecSig = sigKey.sign(hash);
TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false);
if (scriptPubKey.isSentToRawPubKey())
input.setScriptSig(ScriptBuilder.createInputScript(txSig));
else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
input = tx.getInput(1);
scriptPubKey = input.getConnectedOutput().getScriptPubKey();
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
/*
IN[0] offerer signed
IN[1] taker signed
OUT[0] MS
OUT[1] offerer change
OUT[2] taker change
*/
tx.verify();
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
FutureCallback callback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.info("sendResult onSuccess:" + transaction.toString());
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t.toString());
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
}
};
Futures.addCallback(broadcastComplete, callback);
return tx;
}
public void shutDown()
@ -100,6 +316,10 @@ public class WalletFacade implements WalletEventListener
walletAppKit.awaitTerminated();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Listener
///////////////////////////////////////////////////////////////////////////////////////////
public void addDownloadListener(DownloadListener listener)
{
downloadListeners.add(listener);
@ -131,23 +351,40 @@ public class WalletFacade implements WalletEventListener
}
//MOCK
public KeyPair createNewAddress()
{
return new KeyPair(UUID.randomUUID().toString(), UUID.randomUUID().toString());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trading wallet
///////////////////////////////////////////////////////////////////////////////////////////
public BigInteger getBalance()
{
return getWallet().getBalance(Wallet.BalanceType.ESTIMATED);
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
}
public String getAddress()
{
return getWallet().getKeys().get(0).toAddress(networkParameters).toString();
return wallet.getKeys().get(0).toAddress(params).toString();
}
// account registration
public String payFee(BigInteger fee, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
Transaction tx = new Transaction(params);
//TransactionOutput output = new TransactionOutput(params, tx, Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript());
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// give fee to miners yet. Later it could be spent to other traders via lottery...
sendRequest.fee = fee;
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback);
return tx.getHashAsString();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Account registration
///////////////////////////////////////////////////////////////////////////////////////////
public Address getAccountRegistrationAddress()
{
return getAccountRegistrationWallet().getAddress();
@ -155,7 +392,6 @@ public class WalletFacade implements WalletEventListener
public String getAccountRegistrationPubKey()
{
return Utils.bytesToHexString(getAccountRegistrationWallet().getKey().getPubKey());
}
@ -169,14 +405,9 @@ public class WalletFacade implements WalletEventListener
getAccountRegistrationWallet().saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(getAccountRegistrationWallet().getKey(), stringifiedBankAccounts));
}
public boolean verifyAccountRegistration(String address, String hashAsHexStringToVerify, byte[] pubKey, String bankAccountIDs, String signatureBankAccountIDs)
{
return true;
/*
return cryptoFacade.verifySignature(pubKey, bankAccountIDs, signatureBankAccountIDs)
&& cryptoFacade.verifyHash(hashAsHexStringToVerify, bankAccountIDs, signatureBankAccountIDs)
&& blockChainFacade.verifyEmbeddedData(address); */
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
///////////////////////////////////////////////////////////////////////////////////////////
public int getRegConfNumBroadcastPeers()
{
@ -188,7 +419,16 @@ public class WalletFacade implements WalletEventListener
return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet());
}
// WalletEventListener
public ECKey getAccountKey()
{
return getAccountRegistrationWallet().getKey();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletEventListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{
@ -202,7 +442,7 @@ public class WalletFacade implements WalletEventListener
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{
for (WalletListener walletListener : walletListeners)
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(getWallet()));
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(wallet));
log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
}
@ -238,61 +478,33 @@ public class WalletFacade implements WalletEventListener
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private AccountRegistrationWallet getAccountRegistrationWallet()
{
if (accountRegistrationWallet == null)
accountRegistrationWallet = new AccountRegistrationWallet(networkParameters, walletAppKit.chain(), walletAppKit.peerGroup());
accountRegistrationWallet = new AccountRegistrationWallet(params, walletAppKit.chain(), walletAppKit.peerGroup());
return accountRegistrationWallet;
}
public String payOfferFee() throws InsufficientMoneyException
private Script getMultiSigScript(String offererPubKey, String takerPubKey, String arbitratorPubKey)
{
getWallet();
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58(offererPubKey));
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58(takerPubKey));
ECKey arbitratorKey = new ECKey(null, Utils.parseAsHexOrBase58(arbitratorPubKey));
Script script = new ScriptBuilder()
.op(OP_RETURN)
.build();
Transaction transaction = new Transaction(networkParameters);
TransactionOutput dataOutput = new TransactionOutput(networkParameters,
transaction,
Transaction.MIN_NONDUST_OUTPUT,
script.getProgram());
transaction.addOutput(dataOutput);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction);
// give fee to miners yet. Later it could be spent to other traders via lottery...
sendRequest.fee = Fees.OFFER_CREATION_FEE;
Wallet.SendResult sendResult = getWallet().sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction result)
{
log.info("sendResult onSuccess:" + result.toString());
// Platform.runLater(overlayUi::done);
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t.toString());
// We died trying to empty the wallet.
// crashAlert(t);
}
});
return transaction.getHashAsString();
List<ECKey> keys = ImmutableList.of(offererKey, takerKey, arbitratorKey);
return ScriptBuilder.createMultiSigOutputScript(2, keys);
}
private Wallet getWallet()
{
return walletAppKit.wallet();
}
// inner classes
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////////////////////
private class BlockChainDownloadListener extends com.google.bitcoin.core.DownloadListener
{
@Override
@ -326,5 +538,4 @@ public class WalletFacade implements WalletEventListener
void onCoinsReceived(BigInteger newBalance);
}
}

View file

@ -3,10 +3,14 @@ package io.bitsquare.btc;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import java.math.BigInteger;
import java.util.Set;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
public class WalletUtil
{
@ -41,4 +45,11 @@ public class WalletUtil
}
return null;
}
public static Script getEmptyOP_RETURNScript()
{
return new ScriptBuilder()
.op(OP_RETURN)
.build();
}
}

View file

@ -37,11 +37,9 @@ public class CryptoFacade
return Utils.sha256hash160(concatenateChunks(stringifiedBankAccounts, signedBankAccountIDs).getBytes(Charsets.UTF_8));
}
// TODO MOCK
public String signContract(String contractAsJson)
public String signContract(ECKey key, String contractAsJson)
{
return contractAsJson;
return key.signMessage(contractAsJson);
}
// registration

View file

@ -62,7 +62,7 @@ class WalletAppKitProvider implements Provider<WalletAppKit>
public WalletAppKit get()
{
return new WalletAppKit(networkParameters, new File("."), "bitsquare");
return new WalletAppKit(networkParameters, new File("."), WalletFacade.WALLET_PREFIX);
}
}

View file

@ -2,12 +2,12 @@ package io.bitsquare.gui;
import com.google.inject.Inject;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.market.MarketController;
import io.bitsquare.gui.setup.SetupController;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.gui.util.Localisation;
import io.bitsquare.trade.Direction;
@ -251,7 +251,7 @@ public class MainController implements Initializable, NavigationController, Wall
balanceLabel.setMouseTransparent(true);
balanceLabel.setPrefWidth(90);
balanceLabel.setId("nav-balance-label");
balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getBalance(), false));
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
Label balanceCurrencyLabel = new Label("BTC");
balanceCurrencyLabel.setPadding(new Insets(6, 0, 0, 0));

View file

@ -3,10 +3,10 @@ package io.bitsquare.gui.funds;
import com.google.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.util.Formatter;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
@ -63,7 +63,7 @@ public class FundsController implements Initializable, ChildController
this.navigationController = navigationController;
addressLabel.setText(walletFacade.getAddress());
balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getBalance(), false));
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
}

View file

@ -1,7 +1,10 @@
package io.bitsquare.gui.market.offer;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.Inject;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.Fees;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ChildController;
@ -13,6 +16,7 @@ import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trading;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.user.User;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
@ -22,10 +26,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,9 +45,11 @@ public class CreateOfferController implements Initializable, ChildController, Wa
private Settings settings;
private User user;
private Direction direction;
private Offer offer;
private int gridRow;
private Button placeOfferButton;
private int gridRow;
private TextField collateralTextField, minAmountTextField;
@FXML
private AnchorPane holderPane;
@ -54,7 +58,12 @@ public class CreateOfferController implements Initializable, ChildController, Wa
@FXML
public Label buyLabel;
@FXML
public TextField volume, amount, price, minAmount;
public TextField volumeTextField, amountTextField, priceTextField;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public CreateOfferController(Trading trading, WalletFacade walletFacade, Settings settings, User user)
@ -65,86 +74,51 @@ public class CreateOfferController implements Initializable, ChildController, Wa
this.user = user;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public void setOrderBookFilter(OrderBookFilter orderBookFilter)
{
direction = orderBookFilter.getDirection();
amountTextField.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
minAmountTextField.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
priceTextField.setText(Formatter.formatPrice(orderBookFilter.getPrice()));
buyLabel.setText(Formatter.formatDirection(direction, false) + ":");
collateralTextField.setText(Formatter.formatVolume(settings.getMinCollateral()));
updateVolume();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: Initializable
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void initialize(URL url, ResourceBundle rb)
{
walletFacade.addRegistrationWalletListener(this);
gridRow = 2;
FormBuilder.addVSpacer(formGridPane, ++gridRow);
FormBuilder.addHeaderLabel(formGridPane, "Offer details:", ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account type:", Localisation.get(user.getCurrentBankAccount().getBankAccountType().getType().toString()), ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account currency:", user.getCurrentBankAccount().getCurrency().getCurrencyCode(), ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account county:", user.getCurrentBankAccount().getCountryLocale().getDisplayCountry(), ++gridRow);
FormBuilder.addTextField(formGridPane, "Accepted countries:", Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()), ++gridRow);
FormBuilder.addTextField(formGridPane, "Accepted languages:", Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()), ++gridRow);
FormBuilder.addVSpacer(formGridPane, ++gridRow);
Label placeOfferTitle = FormBuilder.addHeaderLabel(formGridPane, "Place offer:", ++gridRow);
TextField feeLabel = FormBuilder.addTextField(formGridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_CREATION_FEE, true), ++gridRow);
feeLabel.setMouseTransparent(true);
placeOfferButton = new Button("Place offer");
formGridPane.add(placeOfferButton, 1, ++gridRow);
placeOfferButton.setDefaultButton(true);
// handlers
amount.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
updateVolume();
}
});
price.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
updateVolume();
}
});
placeOfferButton.setOnAction(e -> {
if (inputValid())
{
Offer offer = new Offer(user.getAccountID(),
user.getMessageID(),
direction,
Converter.stringToDouble(price.getText()),
Converter.stringToDouble(amount.getText()),
Converter.stringToDouble(minAmount.getText()),
user.getCurrentBankAccount().getBankAccountType().getType(),
user.getCurrentBankAccount().getCurrency(),
user.getCurrentBankAccount().getCountryLocale(),
settings.getRandomArbitrator(),
settings.getAcceptedCountryLocales(),
settings.getAcceptedLanguageLocales());
try
{
String txID = trading.placeNewOffer(offer);
formGridPane.getChildren().remove(placeOfferButton);
placeOfferTitle.setText("Transaction sent:");
buildConfirmationView(txID);
} catch (InsufficientMoneyException e1)
{
Dialogs.create()
.title("Not enough money available")
.message("There is not enough money available. Please pay in first to your wallet.")
.nativeTitleBar()
.lightweight()
.showError();
}
}
});
buildScreen();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: ChildController
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void setNavigationController(NavigationController navigationController)
{
this.navigationController = navigationController;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletFacade.WalletListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks)
{
@ -157,11 +131,119 @@ public class CreateOfferController implements Initializable, ChildController, Wa
log.info("onCoinsReceived " + newBalance);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private void buildScreen()
{
gridRow = 1;
minAmountTextField = FormBuilder.addTextField(formGridPane, "Min. Amount:", String.valueOf(settings.getMaxCollateral()), ++gridRow, true, true);
collateralTextField = FormBuilder.addTextField(formGridPane, "Collateral (%):", String.valueOf(settings.getMaxCollateral() * 100), ++gridRow, true, true);
FormBuilder.addVSpacer(formGridPane, ++gridRow);
FormBuilder.addHeaderLabel(formGridPane, "Offer details:", ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account type:", Localisation.get(user.getCurrentBankAccount().getBankAccountType().getType().toString()), ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account currency:", user.getCurrentBankAccount().getCurrency().getCurrencyCode(), ++gridRow);
FormBuilder.addTextField(formGridPane, "Bank account county:", user.getCurrentBankAccount().getCountryLocale().getDisplayCountry(), ++gridRow);
FormBuilder.addTextField(formGridPane, "Accepted countries:", Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()), ++gridRow);
FormBuilder.addTextField(formGridPane, "Accepted languages:", Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()), ++gridRow);
FormBuilder.addVSpacer(formGridPane, ++gridRow);
Label placeOfferTitle = FormBuilder.addHeaderLabel(formGridPane, "Place offer:", ++gridRow);
TextField feeLabel = FormBuilder.addTextField(formGridPane, "Offer fee:", BtcFormatter.formatSatoshis(Fees.OFFER_CREATION_FEE, true), ++gridRow);
feeLabel.setMouseTransparent(true);
placeOfferButton = new Button("Place offer");
formGridPane.add(placeOfferButton, 1, ++gridRow);
placeOfferButton.setDefaultButton(true);
// handlers
amountTextField.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
updateVolume();
}
});
priceTextField.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
updateVolume();
}
});
placeOfferButton.setOnAction(e -> {
if (!inputValid())
{
Popups.openWarningPopup("Invalid input", "Your input is invalid");
return;
}
double collateralAsDouble = Converter.stringToDouble(collateralTextField.getText()) / 100;
Arbitrator arbitrator = settings.getRandomArbitrator(collateralAsDouble, getAmountAsBI());
if (arbitrator == null)
{
Popups.openWarningPopup("No arbitrator available", "No arbitrator from your arbitrator list does match the collateral and amount value.");
return;
}
offer = new Offer(user.getAccountID(),
user.getMessageID(),
direction,
Converter.stringToDouble(priceTextField.getText()),
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
BtcFormatter.stringValueToSatoshis(minAmountTextField.getText()),
user.getCurrentBankAccount().getBankAccountType().getType(),
user.getCurrentBankAccount().getCurrency(),
user.getCurrentBankAccount().getCountryLocale(),
arbitrator,
collateralAsDouble,
settings.getAcceptedCountryLocales(),
settings.getAcceptedLanguageLocales());
FutureCallback callback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.info("sendResult onSuccess:" + transaction.toString());
offer.setOfferPaymentTxID(transaction.getHashAsString());
buildConfirmationView(transaction.getHashAsString());
placeOfferTitle.setText("Transaction sent:");
formGridPane.getChildren().remove(placeOfferButton);
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t.toString());
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
}
};
try
{
trading.placeNewOffer(offer, callback);
} catch (InsufficientMoneyException e1)
{
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
}
});
}
private void buildConfirmationView(String txID)
{
FormBuilder.addTextField(formGridPane, "Transaction ID:", txID, ++gridRow, false, true);
ConfirmationComponent confirmationComponent = new ConfirmationComponent(walletFacade, formGridPane, ++gridRow);
new ConfirmationComponent(walletFacade, formGridPane, ++gridRow);
Button closeButton = new Button("Close");
formGridPane.add(closeButton, 1, ++gridRow);
@ -176,50 +258,36 @@ public class CreateOfferController implements Initializable, ChildController, Wa
});
}
@Override
public void setNavigationController(NavigationController navigationController)
{
this.navigationController = navigationController;
}
public void setOrderBookFilter(OrderBookFilter orderBookFilter)
{
direction = orderBookFilter.getDirection();
amount.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
minAmount.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
price.setText(Formatter.formatPrice(orderBookFilter.getPrice()));
String iconPath = (direction == Direction.BUY) ? Icons.BUY : Icons.SELL;
buyLabel.setText(Formatter.formatDirection(direction, false) + ":");
updateVolume();
}
//TODO
private boolean inputValid()
{
return true;
double priceAsDouble = Converter.stringToDouble(priceTextField.getText());
double minAmountAsDouble = Converter.stringToDouble(minAmountTextField.getText());
double amountAsDouble = Converter.stringToDouble(amountTextField.getText());
double collateralAsDouble = Converter.stringToDouble(collateralTextField.getText());
return priceAsDouble > 0 &&
amountAsDouble > 0 &&
minAmountAsDouble > 0 &&
minAmountAsDouble <= amountAsDouble &&
collateralAsDouble >= settings.getMinCollateral() &&
collateralAsDouble <= settings.getMaxCollateral();
}
private void updateVolume()
{
double amountAsDouble = Converter.stringToDouble(amount.getText());
double priceAsDouble = Converter.stringToDouble(price.getText());
volume.setText(Formatter.formatPrice(amountAsDouble * priceAsDouble));
volumeTextField.setText(Formatter.formatVolume(getVolume()));
}
private Image getConfirmIconImage(int numBroadcastPeers, int depthInBlocks)
private double getVolume()
{
if (depthInBlocks > 0)
return Icons.getIconImage(Icons.getIconIDForConfirmations(depthInBlocks));
else
return Icons.getIconImage(Icons.getIconIDForPeersSeenTx(numBroadcastPeers));
double amountAsDouble = Converter.stringToDouble(amountTextField.getText());
double priceAsDouble = Converter.stringToDouble(priceTextField.getText());
return amountAsDouble * priceAsDouble;
}
private String getConfirmationsText(int registrationConfirmationNumBroadcastPeers, int registrationConfirmationDepthInBlocks)
private BigInteger getAmountAsBI()
{
return registrationConfirmationDepthInBlocks + " confirmation(s) / " + "Seen by " + registrationConfirmationNumBroadcastPeers + " peer(s)";
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
}
}

View file

@ -6,45 +6,34 @@
<AnchorPane fx:id="holderPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.market.offer.CreateOfferController">
<children>
<VBox spacing="10" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="5.0"
AnchorPane.bottomAnchor="10.0">
<GridPane fx:id="formGridPane" vgap="5" hgap="5">
<children>
<Label id="form-header-text" text="Create new offer:" GridPane.rowIndex="0"
GridPane.columnIndex="0"/>
<VBox spacing="10" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="5.0"
AnchorPane.bottomAnchor="10.0">
<GridPane fx:id="formGridPane" vgap="5" hgap="5">
<Label id="form-header-text" text="Create new offer:" GridPane.rowIndex="0"
GridPane.columnIndex="0"/>
<Label fx:id="buyLabel" text="Buy" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<Label fx:id="buyLabel" text="Buy" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" spacing="5"
alignment="CENTER_RIGHT">
<children>
<TextField fx:id="amount" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="BTC for:"/>
<TextField fx:id="price" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="EUR ="/>
<TextField fx:id="volume" prefWidth="70.0" alignment="CENTER_RIGHT"
mouseTransparent="true"/>
<Label text="EUR in total"/>
</children>
</HBox>
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" spacing="5"
alignment="CENTER_RIGHT">
<TextField fx:id="amountTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="BTC for:"/>
<TextField fx:id="priceTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="EUR ="/>
<TextField fx:id="volumeTextField" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"
mouseTransparent="true"/>
<Label text="EUR in total"/>
</HBox>
<Label text="Min. Amount:" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<TextField fx:id="minAmount" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<columnConstraints>
<ColumnConstraints halignment="RIGHT"/>
<ColumnConstraints halignment="LEFT"/>
<ColumnConstraints halignment="LEFT"/>
</columnConstraints>
</GridPane>
</VBox>
</children>
<columnConstraints>
<ColumnConstraints halignment="RIGHT"/>
<ColumnConstraints halignment="LEFT"/>
<ColumnConstraints halignment="LEFT"/>
</columnConstraints>
</GridPane>
</VBox>
</AnchorPane>

View file

@ -2,6 +2,7 @@ package io.bitsquare.gui.market.orderbook;
import com.google.inject.Inject;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.market.offer.CreateOfferController;
@ -30,6 +31,7 @@ import javafx.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.ParseException;
@ -155,9 +157,9 @@ public class OrderBookController implements Initializable, ChildController
String title = orderBookListItem.getOffer().getDirection() == Direction.BUY ? "Trade: Sell Bitcoin" : "Trade: Buy Bitcoin";
TradeController tradeController = (TradeController) navigationController.navigateToView(NavigationController.TRADE, title);
double requestedAmount = orderBookListItem.getOffer().getAmount();
BigInteger requestedAmount = orderBookListItem.getOffer().getAmount();
if (!amount.getText().equals(""))
requestedAmount = Converter.stringToDouble(amount.getText());
requestedAmount = BtcFormatter.stringValueToSatoshis(amount.getText());
tradeController.initWithData(orderBookListItem.getOffer(), requestedAmount);
}

View file

@ -22,7 +22,7 @@ public class OrderBookListItem
this.offer = offer;
this.price.set(Formatter.formatPrice(offer.getPrice()));
this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount(), offer.getMinAmount()));
this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount().doubleValue(), offer.getMinAmount().doubleValue()));
this.volume.set(Formatter.formatVolumeWithMinVolume(offer.getVolume(), offer.getMinVolume()));
}

View file

@ -1,5 +1,8 @@
package io.bitsquare.gui.market.trade;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter;
@ -23,7 +26,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -36,7 +38,7 @@ import java.util.ResourceBundle;
public class TradeController implements Initializable, ChildController, WalletFacade.WalletListener
{
private static final Logger log = LoggerFactory.getLogger(TradeController.class);
private static final int SIM_DELAY = 1000;
private static final int SIM_DELAY = 2000;
private Trading trading;
private WalletFacade walletFacade;
@ -44,14 +46,14 @@ public class TradeController implements Initializable, ChildController, WalletFa
private Offer offer;
private Trade trade;
private Contract contract;
private double requestedAmount;
private BigInteger requestedAmount;
private boolean offererIsOnline;
private int row;
private List<ProcessStepItem> processStepItems = new ArrayList();
private NavigationController navigationController;
private TextField amountTextField, totalToPayLabel, totalLabel;
private TextField amountTextField, totalToPayLabel, totalLabel, collateralTextField;
private Label statusTextField, infoLabel;
private Button nextButton;
private ProgressBar progressBar;
@ -81,14 +83,14 @@ public class TradeController implements Initializable, ChildController, WalletFa
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public void initWithData(Offer offer, double requestedAmount)
public void initWithData(Offer offer, BigInteger requestedAmount)
{
this.offer = offer;
this.requestedAmount = requestedAmount > 0 ? requestedAmount : offer.getAmount();
this.requestedAmount = requestedAmount.compareTo(BigInteger.ZERO) > 0 ? requestedAmount : offer.getAmount();
trade = trading.createNewTrade(offer);
trade = trading.createTrade(offer);
trade.setTradeAmount(requestedAmount);
contract = trading.createNewContract(trade);
contract = trading.createContract(trade);
processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC"));
processStepItems.add(new ProcessStepItem("Bank transfer"));
@ -148,19 +150,22 @@ public class TradeController implements Initializable, ChildController, WalletFa
row = -1;
FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row);
amountTextField = FormBuilder.addTextField(gridPane, "Amount BTC:", Formatter.formatAmount(requestedAmount), ++row, true, true);
amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.formatSatoshis(requestedAmount, false), ++row, true, true);
amountTextField.textProperty().addListener(e -> {
applyVolume();
applyCollateral();
totalToPayLabel.setText(getTotalToPay());
});
Label amountRangeLabel = new Label("(" + Formatter.formatAmount(offer.getMinAmount()) + " - " + Formatter.formatAmount(offer.getAmount()) + ")");
Label amountRangeLabel = new Label("(" + BtcFormatter.formatSatoshis(offer.getMinAmount(), false) + " - " + BtcFormatter.formatSatoshis(offer.getAmount(), false) + ")");
gridPane.add(amountRangeLabel, 2, row);
FormBuilder.addTextField(gridPane, "Price:", Formatter.formatPriceWithCurrencyPair(offer.getPrice(), offer.getCurrency()), ++row);
totalLabel = FormBuilder.addTextField(gridPane, "Total:", Formatter.formatVolume(getVolume(), offer.getCurrency()), ++row);
FormBuilder.addTextField(gridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_TAKER_FEE, true), ++row);
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay:", getTotalToPay(), ++row);
FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", Formatter.formatPrice(offer.getPrice()), ++row);
totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row);
collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row);
applyCollateral();
FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE, false), ++row);
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPay(), ++row);
nextButton = FormBuilder.addButton(gridPane, "Take offer and pay", ++row);
nextButton.setDefaultButton(true);
@ -247,9 +252,9 @@ public class TradeController implements Initializable, ChildController, WalletFa
progressIndicatorHolder.getChildren().addAll(progressIndicator);
gridPane.add(progressIndicatorHolder, 1, row);
trade.setTradeAmount(Converter.stringToDouble(amountTextField.getText()));
trading.sendTakeOfferRequest(trade);
trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
trading.sendTakeOfferRequest(trade);
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
onTakeOfferRequestConfirmed();
progressBar.setProgress(1.0 / 3.0);
@ -259,14 +264,37 @@ public class TradeController implements Initializable, ChildController, WalletFa
private void onTakeOfferRequestConfirmed()
{
trading.payOfferFee(trade);
FutureCallback callback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.info("sendResult onSuccess:" + transaction.toString());
trade.setTakeOfferFeeTxID(transaction.getHashAsString());
statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
onOfferFeePaymentConfirmed();
progressBar.setProgress(2.0 / 3.0);
return null;
});
statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
onOfferFeePaymentConfirmed();
progressBar.setProgress(2.0 / 3.0);
return null;
});
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t.toString());
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
}
};
try
{
trading.payOfferFee(trade, callback);
} catch (InsufficientMoneyException e)
{
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
}
}
private void onOfferFeePaymentConfirmed()
@ -282,14 +310,10 @@ public class TradeController implements Initializable, ChildController, WalletFa
private void onUserDetailsReceived()
{
if (!walletFacade.verifyAccountRegistration(offer.getAccountID(), null, null, null, null))
if (!blockChainFacade.verifyEmbeddedData(offer.getAccountID()))
{
Dialogs.create()
.title("Offerers bank account is blacklisted")
.message("Offerers bank account is blacklisted.")
.nativeTitleBar()
.lightweight()
.showError();
Popups.openErrorPopup("Offerers bank account is blacklisted", "Offerers bank account is blacklisted.");
return;
}
trading.signContract(contract);
@ -337,8 +361,19 @@ public class TradeController implements Initializable, ChildController, WalletFa
gridPane.getChildren().clear();
row = -1;
FormBuilder.addHeaderLabel(gridPane, "Trade successfully completed", ++row);
FormBuilder.addTextField(gridPane, "You have payed:", getTotalToPay(), ++row);
FormBuilder.addTextField(gridPane, "You have received:", getTotalToReceive(), ++row);
FormBuilder.addTextField(gridPane, "You have payed in total (BTC):", getTotalToPay(), ++row);
if (takerIsSelling())
{
FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false), ++row);
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row);
}
else
{
FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false), ++row);
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row);
FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.formatSatoshis(offer.getAmount(), false), ++row);
}
gridPane.add(nextButton, 1, ++row);
}
@ -355,8 +390,8 @@ public class TradeController implements Initializable, ChildController, WalletFa
// Other Private methods
private boolean tradeAmountValid()
{
double tradeAmount = Converter.stringToDouble(amountTextField.getText());
return tradeAmount <= offer.getAmount() && tradeAmount >= offer.getMinAmount();
BigInteger tradeAmount = BtcFormatter.stringValueToSatoshis(amountTextField.getText());
return tradeAmount.compareTo(offer.getAmount()) <= 0 && tradeAmount.compareTo(offer.getMinAmount()) >= 0;
}
private boolean takerIsSelling()
@ -388,28 +423,31 @@ public class TradeController implements Initializable, ChildController, WalletFa
private String getTotalToPay()
{
String result = "";
if (takerIsSelling())
{
double btcValue = Converter.stringToDouble(amountTextField.getText()) + BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE)/* +
offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/;
result = Formatter.formatAmount(btcValue, true, true);
return BtcFormatter.formatSatoshis(getAmountInSatoshis().add(Fees.OFFER_TAKER_FEE).add(getCollateralInSatoshis()), false);
}
else
{
double btcValue = BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE) /*+ offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/;
result = Formatter.formatAmount(btcValue, true, true) + "\n" + Formatter.formatVolume(getVolume(), offer.getCurrency());
return BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE.add(getCollateralInSatoshis()), false) + "\n" +
Formatter.formatVolume(getVolume(), offer.getCurrency());
}
return result;
}
private String getTotalToReceive()
private void applyCollateral()
{
if (takerIsSelling())
return Formatter.formatVolume(getVolume(), offer.getCurrency());
else
return Formatter.formatAmount(offer.getAmount(), true, true);
collateralTextField.setText(BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false));
}
private BigInteger getCollateralInSatoshis()
{
return BtcFormatter.doubleValueToSatoshis(Converter.stringToDouble(amountTextField.getText()) * offer.getCollateral());
}
private BigInteger getAmountInSatoshis()
{
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
}
}

View file

@ -9,24 +9,14 @@ public class MockDelay
{
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Task(expectedMsg));
// max timeout 5 sec
try
{
try
{
// max timeout 5 sec
return future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
} catch (TimeoutException e)
return future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | TimeoutException | ExecutionException e)
{
System.out.println("Terminated!");
e.printStackTrace();
}
executor.shutdownNow();
return null;
}

View file

@ -6,6 +6,7 @@ import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController;
@ -14,8 +15,8 @@ import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.components.processbar.ProcessStepBar;
import io.bitsquare.gui.components.processbar.ProcessStepItem;
import io.bitsquare.gui.util.FormBuilder;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.gui.util.Localisation;
import io.bitsquare.gui.util.Popups;
import io.bitsquare.gui.util.Verification;
import io.bitsquare.storage.Storage;
import io.bitsquare.user.User;
@ -29,7 +30,6 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.util.StringConverter;
import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -41,13 +41,15 @@ public class SetupController implements Initializable, ChildController, WalletFa
{
private static final Logger log = LoggerFactory.getLogger(SetupController.class);
private User user;
private final User user;
private final WalletFacade walletFacade;
private Storage storage;
private List<ProcessStepItem> processStepItems = new ArrayList();
private final Storage storage;
private final List<ProcessStepItem> processStepItems = new ArrayList<>();
private NavigationController navigationController;
private TextField balanceLabel, accountTitle, accountHolderName, accountPrimaryID, accountSecondaryID;
private ComboBox countryComboBox, bankTransferTypeComboBox, currencyComboBox;
private ComboBox<Locale> countryComboBox;
private ComboBox<BankAccountType> bankTransferTypeComboBox;
private ComboBox<Currency> currencyComboBox;
private Button addBankAccountButton;
@FXML
@ -133,7 +135,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
public void onCoinsReceived(BigInteger newBalance)
{
updateCreateAccountButton();
balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true));
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true));
log.info("onCoinsReceived " + newBalance);
}
@ -161,7 +163,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard"));
balanceLabel = FormBuilder.addTextField(gridPane, "Balance:", Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
balanceLabel = FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
new ConfirmationComponent(walletFacade, gridPane, ++row);
@ -194,7 +196,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
gridPane.getChildren().clear();
int row = -1;
bankTransferTypeComboBox = FormBuilder.addComboBox(gridPane, "Bank account type:", Utils.getAllBankAccountTypes(), ++row);
bankTransferTypeComboBox = FormBuilder.addBankAccountComboBox(gridPane, "Bank account type:", Utils.getAllBankAccountTypes(), ++row);
bankTransferTypeComboBox.setConverter(new StringConverter<BankAccountType>()
{
@Override
@ -216,7 +218,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
accountPrimaryID = FormBuilder.addInputField(gridPane, "Bank account primary ID", "", ++row);
accountSecondaryID = FormBuilder.addInputField(gridPane, "Bank account secondary ID:", "", ++row);
currencyComboBox = FormBuilder.addComboBox(gridPane, "Currency used for bank account:", Utils.getAllCurrencies(), ++row);
currencyComboBox = FormBuilder.addCurrencyComboBox(gridPane, "Currency used for bank account:", Utils.getAllCurrencies(), ++row);
currencyComboBox.setPromptText("Select currency");
currencyComboBox.setConverter(new StringConverter<Currency>()
{
@ -233,7 +235,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
}
});
countryComboBox = FormBuilder.addComboBox(gridPane, "Country of bank account:", Utils.getAllLocales(), ++row);
countryComboBox = FormBuilder.addLocalesComboBox(gridPane, "Country of bank account:", Utils.getAllLocales(), ++row);
countryComboBox.setPromptText("Select country");
countryComboBox.setConverter(new StringConverter<Locale>()
{
@ -267,11 +269,10 @@ public class SetupController implements Initializable, ChildController, WalletFa
bankTransferTypeComboBox.valueProperty().addListener((ov, oldValue, newValue) -> {
if (newValue != null && newValue instanceof BankAccountType)
{
BankAccountType bankAccountType = (BankAccountType) newValue;
accountPrimaryID.setText("");
accountPrimaryID.setPromptText(bankAccountType.getPrimaryIDName());
accountPrimaryID.setPromptText(newValue.getPrimaryIDName());
accountSecondaryID.setText("");
accountSecondaryID.setPromptText(bankAccountType.getSecondaryIDName());
accountSecondaryID.setPromptText(newValue.getSecondaryIDName());
checkCreateAccountButtonState();
}
@ -303,19 +304,14 @@ public class SetupController implements Initializable, ChildController, WalletFa
{
walletFacade.sendRegistrationTx(user.getStringifiedBankAccounts());
user.setAccountID(walletFacade.getAccountRegistrationAddress().toString());
user.setMessageID(walletFacade.getAccountRegistrationPubKey().toString());
user.setMessageID(walletFacade.getAccountRegistrationPubKey());
storage.write(user.getClass().getName(), user);
processStepBar.next();
buildStep2();
} catch (InsufficientMoneyException e1)
{
Dialogs.create()
.title("Not enough money available")
.message("There is not enough money available. Please pay in first to your wallet.")
.nativeTitleBar()
.lightweight()
.showError();
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
}
}
});
@ -334,7 +330,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
FormBuilder.addHeaderLabel(gridPane, "Registration complete", ++row);
FormBuilder.addTextField(gridPane, "Registration address:", walletFacade.getAccountRegistrationAddress().toString(), ++row);
FormBuilder.addTextField(gridPane, "Balance:", Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
Button closeButton = FormBuilder.addButton(gridPane, "Close", ++row);
closeButton.setDefaultButton(true);
@ -409,9 +405,9 @@ public class SetupController implements Initializable, ChildController, WalletFa
if (verifyBankAccountData())
{
BankAccount bankAccount = new BankAccount(
(BankAccountType) bankTransferTypeComboBox.getSelectionModel().getSelectedItem(),
(Currency) currencyComboBox.getSelectionModel().getSelectedItem(),
(Locale) countryComboBox.getSelectionModel().getSelectedItem(),
bankTransferTypeComboBox.getSelectionModel().getSelectedItem(),
currencyComboBox.getSelectionModel().getSelectedItem(),
countryComboBox.getSelectionModel().getSelectedItem(),
accountTitle.getText(),
accountHolderName.getText(),
accountPrimaryID.getText(),

View file

@ -7,25 +7,23 @@
xmlns:fx="http://javafx.com/fxml">
<ScrollPane fitToWidth="true" AnchorPane.leftAnchor="10" AnchorPane.rightAnchor="10" AnchorPane.topAnchor="10"
AnchorPane.bottomAnchor="30">
<content>
<VBox fx:id="vBox" spacing="10">
<Label text="Setup trading account" id="headline-label"/>
<ProcessStepBar fx:id="processStepBar"/>
<VSpacer prefHeight="10"/>
<Label fx:id="infoLabel"/>
<GridPane fx:id="gridPane" vgap="5" hgap="5">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<columnConstraints>
<ColumnConstraints halignment="RIGHT"/>
<ColumnConstraints halignment="LEFT" prefWidth="320"/>
<ColumnConstraints halignment="LEFT"/>
</columnConstraints>
</GridPane>
<Button fx:id="nextButton" defaultButton="true"/>
<Button fx:id="skipButton"/>
</VBox>
</content>
<VBox fx:id="vBox" spacing="10">
<Label text="Setup trading account" id="headline-label"/>
<ProcessStepBar fx:id="processStepBar"/>
<VSpacer prefHeight="10"/>
<Label fx:id="infoLabel"/>
<GridPane fx:id="gridPane" vgap="5" hgap="5">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding>
<columnConstraints>
<ColumnConstraints halignment="RIGHT"/>
<ColumnConstraints halignment="LEFT" prefWidth="320"/>
<ColumnConstraints halignment="LEFT"/>
</columnConstraints>
</GridPane>
<Button fx:id="nextButton" defaultButton="true"/>
<Button fx:id="skipButton"/>
</VBox>
</ScrollPane>
</AnchorPane>

View file

@ -20,9 +20,8 @@ public class Converter
} catch (ParseException e)
{
log.warn(e.toString());
return 0;
}
return 0;
}
}

View file

@ -1,5 +1,6 @@
package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.components.VSpacer;
import javafx.collections.FXCollections;
@ -8,7 +9,9 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
public class FormBuilder
{
@ -67,14 +70,38 @@ public class FormBuilder
return button;
}
public static ComboBox addComboBox(GridPane gridPane, String title, List<?> list, int row)
public static ComboBox<Locale> addLocalesComboBox(GridPane gridPane, String title, List<Locale> list, int row)
{
gridPane.add(new Label(title), 0, row);
ComboBox comboBox = new ComboBox(FXCollections.observableArrayList(list));
ComboBox<Locale> comboBox = new ComboBox<>(FXCollections.observableArrayList(list));
gridPane.add(comboBox, 1, row);
return comboBox;
}
public static ComboBox<Currency> addCurrencyComboBox(GridPane gridPane, String title, List<Currency> list, int row)
{
gridPane.add(new Label(title), 0, row);
ComboBox<Currency> comboBox = new ComboBox<>(FXCollections.observableArrayList(list));
gridPane.add(comboBox, 1, row);
return comboBox;
}
public static ComboBox<BankAccountType> addBankAccountComboBox(GridPane gridPane, String title, List<BankAccountType> list, int row)
{
gridPane.add(new Label(title), 0, row);
ComboBox<BankAccountType> comboBox = new ComboBox<>(FXCollections.observableArrayList(list));
gridPane.add(comboBox, 1, row);
return comboBox;
}
/* public static ComboBox addLocalesComboBox(GridPane gridPane, String title, List<?> list, int row)
{
gridPane.add(new Label(title), 0, row);
ComboBox<?> comboBox = new ComboBox<>(FXCollections.observableArrayList(list));
gridPane.add(comboBox, 1, row);
return comboBox;
} */
public static TextField addConfirmationsLabel(GridPane gridPane, WalletFacade walletFacade, int row)
{

View file

@ -1,9 +1,7 @@
package io.bitsquare.gui.util;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.trade.Direction;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Currency;
import java.util.List;
@ -88,14 +86,6 @@ public class Formatter
return s.substring(1, s.length() - 1);
}
public static String formatSatoshis(BigInteger satoshis, boolean useBTC)
{
if (useBTC)
return formatDouble(BtcFormatter.satoshiToBTC(satoshis), 4) + " BTC";
else
return formatDouble(BtcFormatter.satoshiToBTC(satoshis), 4);
}
public static String formatDouble(double value)
{
return formatDouble(value, 2);

View file

@ -13,4 +13,14 @@ public class Popups
.lightweight()
.showError();
}
public static void openWarningPopup(String title, String message)
{
Dialogs.create()
.title(title)
.message(message)
.nativeTitleBar()
.lightweight()
.showWarning();
}
}

View file

@ -12,16 +12,16 @@ import static com.google.common.base.Preconditions.checkState;
public class Transitions
{
public static final int UI_ANIMATION_TIME_MSEC = 350;
public static final int UI_ANIMATION_TIME = 350;
public static void fadeIn(Node ui)
{
fadeIn(ui, UI_ANIMATION_TIME_MSEC);
fadeIn(ui, UI_ANIMATION_TIME);
}
public static void fadeIn(Node ui, int time)
{
FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME_MSEC), ui);
FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME), ui);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
@ -29,7 +29,7 @@ public class Transitions
public static Animation fadeOut(Node ui)
{
FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME_MSEC), ui);
FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME), ui);
ft.setFromValue(ui.getOpacity());
ft.setToValue(0.0);
ft.play();
@ -45,7 +45,7 @@ public class Transitions
public static void blurOut(Node node)
{
blurOut(node, UI_ANIMATION_TIME_MSEC);
blurOut(node, UI_ANIMATION_TIME);
}
public static void blurOut(Node node, int time)
@ -64,7 +64,7 @@ public class Transitions
GaussianBlur blur = (GaussianBlur) node.getEffect();
Timeline timeline = new Timeline();
KeyValue kv = new KeyValue(blur.radiusProperty(), 0.0);
KeyFrame kf = new KeyFrame(Duration.millis(UI_ANIMATION_TIME_MSEC), kv);
KeyFrame kf = new KeyFrame(Duration.millis(UI_ANIMATION_TIME), kv);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(actionEvent -> node.setEffect(null));
timeline.play();

View file

@ -49,7 +49,7 @@ public class Message
public String toString()
{
return type + ": " + Utils.convertToJson(payload);
return type + ": " + Utils.objectToJson(payload);
}

View file

@ -4,6 +4,7 @@ import com.google.inject.Inject;
import io.bitsquare.user.Arbitrator;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@ -14,8 +15,9 @@ public class Settings implements Serializable
private List<Locale> acceptedLanguageLocales = new ArrayList<>();
private List<Locale> acceptedCountryLocales = new ArrayList<>();
private List<Arbitrator> arbitrators = new ArrayList<>();
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
private double maxCollateral;
private double minCollateral;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -37,7 +39,9 @@ public class Settings implements Serializable
{
acceptedLanguageLocales = savedSettings.getAcceptedLanguageLocales();
acceptedCountryLocales = savedSettings.getAcceptedCountryLocales();
arbitrators = savedSettings.getArbitrators();
acceptedArbitrators = savedSettings.getAcceptedArbitrators();
maxCollateral = savedSettings.getMaxCollateral();
minCollateral = savedSettings.getMinCollateral();
}
}
@ -53,20 +57,29 @@ public class Settings implements Serializable
acceptedCountryLocales.add(locale);
}
public void addArbitrator(Arbitrator arbitrator)
public void addAcceptedArbitrator(Arbitrator arbitrator)
{
if (!arbitrators.contains(arbitrator))
arbitrators.add(arbitrator);
if (!acceptedArbitrators.contains(arbitrator))
acceptedArbitrators.add(arbitrator);
}
public void setMaxCollateral(double maxCollateral)
{
this.maxCollateral = maxCollateral;
}
public void setMinCollateral(double minCollateral)
{
this.minCollateral = minCollateral;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public List<Arbitrator> getArbitrators()
public List<Arbitrator> getAcceptedArbitrators()
{
return arbitrators;
return acceptedArbitrators;
}
public List<Locale> getAcceptedLanguageLocales()
@ -79,8 +92,28 @@ public class Settings implements Serializable
return acceptedCountryLocales;
}
public Arbitrator getRandomArbitrator()
public Arbitrator getRandomArbitrator(double collateral, BigInteger amount)
{
return arbitrators.size() > 0 ? arbitrators.get((int) (Math.random() * arbitrators.size())) : null;
List<Arbitrator> candidates = new ArrayList<>();
for (Arbitrator arbitrator : acceptedArbitrators)
{
if (arbitrator.getArbitrationFeePercent() >= collateral &&
arbitrator.getMinArbitrationFee().compareTo(amount) < 0)
{
candidates.add(arbitrator);
}
}
return candidates.size() > 0 ? candidates.get((int) (Math.random() * candidates.size())) : null;
}
public double getMaxCollateral()
{
return maxCollateral;
}
public double getMinCollateral()
{
return minCollateral;
}
}

View file

@ -3,6 +3,7 @@ package io.bitsquare.trade;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.user.Arbitrator;
import java.math.BigInteger;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
@ -12,29 +13,37 @@ public class Offer
{
private UUID uid;
private double price;
private double amount;
private double minAmount;
private BigInteger amount;
private BigInteger minAmount;
private String accountID;
private String messageID;
private Direction direction;
private BankAccountType.BankAccountTypeEnum bankAccountTypeEnum;
private Currency currency;
private Locale bankAccountCountryLocale;
private double collateral;
private List<Locale> acceptedCountryLocales;
private List<Locale> acceptedLanguageLocales;
private String offerPaymentTxID;
private Arbitrator arbitrator;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Offer(String accountID,
String messageID,
Direction direction,
double price,
double amount,
double minAmount,
BigInteger amount,
BigInteger minAmount,
BankAccountType.BankAccountTypeEnum bankAccountTypeEnum,
Currency currency,
Locale bankAccountCountryLocale,
Arbitrator arbitrator,
double collateral,
List<Locale> acceptedCountryLocales,
List<Locale> acceptedLanguageLocales)
{
@ -48,6 +57,7 @@ public class Offer
this.currency = currency;
this.bankAccountCountryLocale = bankAccountCountryLocale;
this.arbitrator = arbitrator;
this.collateral = collateral;
this.acceptedCountryLocales = acceptedCountryLocales;
this.acceptedLanguageLocales = acceptedLanguageLocales;
@ -89,12 +99,12 @@ public class Offer
return price;
}
public double getAmount()
public BigInteger getAmount()
{
return amount;
}
public double getMinAmount()
public BigInteger getMinAmount()
{
return minAmount;
}
@ -131,12 +141,12 @@ public class Offer
public double getVolume()
{
return price * amount;
return price * amount.doubleValue();
}
public double getMinVolume()
{
return price * minAmount;
return price * minAmount.doubleValue();
}
public String getOfferPaymentTxID()
@ -148,4 +158,9 @@ public class Offer
{
return arbitrator;
}
public double getCollateral()
{
return collateral;
}
}

View file

@ -1,5 +1,6 @@
package io.bitsquare.trade;
import java.math.BigInteger;
import java.util.UUID;
public class Trade
@ -7,8 +8,7 @@ public class Trade
private Offer offer;
private boolean takeOfferRequested;
private boolean takeOfferAccepted;
private double requestedAmount;
private boolean takeOfferFeePayed;
private BigInteger requestedAmount;
private boolean takeOfferFeePaymentConfirmed;
private String jsonRepresentation;
private String signature;
@ -59,21 +59,16 @@ public class Trade
this.takeOfferAccepted = takeOfferAccepted;
}
public double getRequestedAmount()
public BigInteger getRequestedAmount()
{
return requestedAmount;
}
public void setTradeAmount(double requestedAmount)
public void setTradeAmount(BigInteger requestedAmount)
{
this.requestedAmount = requestedAmount;
}
public void setTakeOfferFeePayed(boolean takeOfferFeePayed)
{
this.takeOfferFeePayed = takeOfferFeePayed;
}
public void setTakeOfferFeePaymentConfirmed(boolean takeOfferFeePaymentConfirmed)
{
this.takeOfferFeePaymentConfirmed = takeOfferFeePaymentConfirmed;

View file

@ -1,8 +1,11 @@
package io.bitsquare.trade;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.Fees;
import io.bitsquare.btc.KeyPair;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
@ -18,8 +21,9 @@ import java.util.HashMap;
import java.util.UUID;
/**
* Main facade for operating with trade domain between GUI and services (msg, btc)
* Represents trade domain. Keeps complexity of process apart from view controller
*/
//TODO use scheduler/process pattern with tasks for every async job
public class Trading
{
private static final Logger log = LoggerFactory.getLogger(Trading.class);
@ -34,6 +38,11 @@ public class Trading
private CryptoFacade cryptoFacade;
private Settings settings;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public Trading(User user,
Settings settings,
@ -50,64 +59,21 @@ public class Trading
this.cryptoFacade = cryptoFacade;
}
/**
* @param offer
*/
public String placeNewOffer(Offer offer) throws InsufficientMoneyException
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void placeNewOffer(Offer offer, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
log.info("place New Offer");
offers.put(offer.getUid().toString(), offer);
String txID = walletFacade.payOfferFee();
offer.setOfferPaymentTxID(txID);
walletFacade.payFee(Fees.OFFER_CREATION_FEE, callback);
messageFacade.broadcast(new Message(Message.BROADCAST_NEW_OFFER, offer));
return txID;
}
/**
* Taker requests offerer to take the offer
*
* @param trade
*/
public void sendTakeOfferRequest(Trade trade)
{
log.info("Taker asks offerer to take his offer");
//messageFacade.send(new Message(Message.REQUEST_TAKE_OFFER, trade), trade.getOffer().getOfferer().getMessageID());
}
/**
* @param trade
* @return
*/
public Contract createNewContract(Trade trade)
{
log.info("create new contract");
KeyPair address = walletFacade.createNewAddress();
Contract contract = new Contract(user, trade, address.getPubKey());
contracts.put(trade.getUid().toString(), contract);
return contract;
}
/**
* @param contract
*/
public void signContract(Contract contract)
{
log.info("sign Contract");
String contractAsJson = Utils.convertToJson(contract);
contract.getTrade().setJsonRepresentation(contractAsJson);
contract.getTrade().setSignature(cryptoFacade.signContract(contractAsJson));
}
/**
* @param offer
* @return
*/
public Trade createNewTrade(Offer offer)
public Trade createTrade(Offer offer)
{
log.info("create New Trade");
Trade trade = new Trade(offer);
@ -115,55 +81,63 @@ public class Trading
return trade;
}
public HashMap<String, Trade> getTrades()
public Contract createContract(Trade trade)
{
return trades;
log.info("create new contract");
KeyPair address = new KeyPair(UUID.randomUUID().toString(), UUID.randomUUID().toString());
//TODO
Contract contract = new Contract(user, trade, address.getPubKey());
contracts.put(trade.getUid().toString(), contract);
return contract;
}
/**
* @param trade
*/
public void payOfferFee(Trade trade)
// trade process
// 1
public void sendTakeOfferRequest(Trade trade)
{
log.info("Taker asks offerer to take his offer");
//messageFacade.send(new Message(Message.REQUEST_TAKE_OFFER, trade), trade.getOffer().getOfferer().getMessageID());
}
// 2
public void payOfferFee(Trade trade, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
log.info("Pay offer fee");
trade.setTakeOfferFeePayed(true);
walletFacade.payFee(Fees.OFFER_TAKER_FEE, callback);
String txID = UUID.randomUUID().toString();
trade.setTakeOfferFeePayed(true);
trade.setTakeOfferFeeTxID(txID);
log.info("Taker asks offerer for confirmation for his fee payment. txID=" + txID);
log.info("Taker asks offerer for confirmation for his fee payment.");
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
// 3
public void requestOffererDetailData()
{
log.info("Request offerer detail data");
}
/**
* @param trade
*/
// 4
public void signContract(Contract contract)
{
log.info("sign Contract");
String contractAsJson = Utils.objectToJson(contract);
contract.getTrade().setJsonRepresentation(contractAsJson);
contract.getTrade().setSignature(cryptoFacade.signContract(walletFacade.getAccountKey(), contractAsJson));
}
// 5
public void payToDepositTx(Trade trade)
{
log.info("create MultiSig address");
log.info("Create deposit tx");
log.info("Sign deposit tx");
log.info("Send deposit Tx");
//walletFacade.takerAddPaymentAndSign();
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
/**
* @param trade
*/
// 6
public void releaseBTC(Trade trade)
{
log.info("Sign payment tx");
@ -174,5 +148,13 @@ public class Trading
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public HashMap<String, Trade> getTrades()
{
return trades;
}
}

View file

@ -1,6 +1,7 @@
package io.bitsquare.trade.orderbook;
import com.google.inject.Inject;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.gui.market.orderbook.OrderBookListItem;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
@ -42,19 +43,20 @@ public class MockOrderBook extends OrderBook
direction = Direction.SELL;
price = 500 - Math.random() * 50;
}
double collateral = 0.1;// Math.random() * 20 + 0.1;
Offer offer = new Offer("mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj",
UUID.randomUUID().toString(),
direction,
price,
amount,
minAmount,
MockData.getBankTransferTypeEnums().get(0),
MockData.getCurrencies().get(0),
MockData.getLocales().get(0),
MockData.getArbitrators().get(0),
MockData.getLocales(),
MockData.getLocales());
BtcFormatter.doubleValueToSatoshis(amount),
BtcFormatter.doubleValueToSatoshis(minAmount),
MockData.getRandomBankTransferTypeEnums().get(0),
MockData.getRandomCurrencies().get(0),
MockData.getRandomLocales().get(0),
MockData.getRandomArbitrators().get(0),
collateral,
MockData.getRandomLocales(),
MockData.getRandomLocales());
return offer;
}

View file

@ -6,6 +6,7 @@ import io.bitsquare.gui.market.orderbook.OrderBookListItem;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.user.User;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@ -76,7 +77,7 @@ public class OrderBook
// The requested amount must be lower or equal then the offer amount
boolean amountResult = true;
if (orderBookFilter.getAmount() > 0)
amountResult = orderBookFilter.getAmount() <= offer.getAmount();
amountResult = orderBookFilter.getAmount() <= offer.getAmount().doubleValue();
// The requested trade direction must be opposite of the offerList trade direction
boolean directionResult = !orderBookFilter.getDirection().equals(offer.getDirection());
@ -91,40 +92,46 @@ public class OrderBook
priceResult = orderBookFilter.getPrice() <= offer.getPrice();
}
// The arbitrator defined in the offer must match one of the accepted arbitrators defined in the settings (1 to n)
boolean arbitratorResult = arbitratorInList(offer.getArbitrator(), settings.getAcceptedArbitrators());
boolean result = currencyResult
&& countryResult
&& languageResult
&& amountResult
&& directionResult
&& priceResult;
&& priceResult
&& arbitratorResult;
/*
/*
log.debug("result = " + result +
", currencyResult = " + currencyResult +
", countryResult = " + countryResult +
", languageResult = " + languageResult +
", bankAccountTypeEnumResult = " + bankAccountTypeEnumResult +
", amountResult = " + amountResult +
", directionResult = " + directionResult +
", priceResult = " + priceResult
", priceResult = " + priceResult +
", arbitratorResult = " + arbitratorResult
);
log.debug("currentBankAccount.getCurrency() = " + currentBankAccount.getCurrency() +
", offer.getCurrency() = " + offer.getCurrency());
log.debug("offer.getCountryLocale() = " + offer.getCountryLocale() +
log.debug("offer.getCountryLocale() = " + offer.getBankAccountCountryLocale() +
", settings.getAcceptedCountryLocales() = " + settings.getAcceptedCountryLocales().toString());
log.debug("settings.getAcceptedLanguageLocales() = " + settings.getAcceptedLanguageLocales() +
", constraints.getAcceptedLanguageLocales() = " + constraints.getLanguageLocales());
", offer.getAcceptedLanguageLocales() = " + offer.getAcceptedLanguageLocales());
log.debug("currentBankAccount.getBankAccountType().getType() = " + currentBankAccount.getBankAccountType().getType() +
", constraints.getBankAccountTypes() = " + constraints.getBankAccountTypes());
", offer.getBankAccountTypeEnum() = " + offer.getBankAccountTypeEnum());
log.debug("orderBookFilter.getAmount() = " + orderBookFilter.getAmount() +
", offer.getAmount() = " + offer.getAmount());
log.debug("orderBookFilter.getDirection() = " + orderBookFilter.getDirection() +
", offer.getDirection() = " + offer.getDirection());
log.debug("orderBookFilter.getPrice() = " + orderBookFilter.getPrice() +
", offer.getPrice() = " + offer.getPrice());
*/
log.debug("offer.getArbitrator() = " + offer.getArbitrator() +
", settings.getAcceptedArbitrators() = " + settings.getAcceptedArbitrators());
*/
return result;
}
});
@ -144,26 +151,45 @@ public class OrderBook
// Private Methods
///////////////////////////////////////////////////////////////////////////////////////////
private boolean countryInList(Locale orderBookFilterLocale, List<Locale> offerConstraintsLocales)
private boolean countryInList(Locale localeToMatch, List<Locale> list)
{
for (Locale locale : offerConstraintsLocales)
for (Locale locale : list)
{
if (locale.getCountry().equals(orderBookFilterLocale.getCountry()))
if (locale.getCountry().equals(localeToMatch.getCountry()))
return true;
}
return false;
}
private boolean languagesInList(List<Locale> orderBookFilterLocales, List<Locale> offerConstraintsLocales)
private boolean languagesInList(List<Locale> list1, List<Locale> list2)
{
for (Locale offerConstraintsLocale : offerConstraintsLocales)
for (Locale locale1 : list2)
{
for (Locale orderBookFilterLocale : orderBookFilterLocales)
for (Locale locale2 : list1)
{
if (offerConstraintsLocale.getLanguage().equals(orderBookFilterLocale.getLanguage()))
if (locale1.getLanguage().equals(locale2.getLanguage()))
return true;
}
}
return false;
}
private boolean arbitratorInList(Arbitrator arbitratorToMatch, List<Arbitrator> list)
{
if (arbitratorToMatch != null)
{
for (Arbitrator arbitrator : list)
{
try
{
if (arbitrator.getUID().equals(arbitratorToMatch.getUID()))
return true;
} catch (Exception e)
{
log.error(e.toString());
}
}
}
return false;
}
}

View file

@ -1,6 +1,7 @@
package io.bitsquare.user;
import java.io.Serializable;
import java.math.BigInteger;
public class Arbitrator implements Serializable
{
@ -11,13 +12,22 @@ public class Arbitrator implements Serializable
private String messageID;
private String url;
public Arbitrator(String name, String pubKey, String messageID, String url)
{
private double baseFeePercent;
private double arbitrationFeePercent;
private BigInteger minArbitrationFee;
private String uid;
public Arbitrator(String uid, String name, String pubKey, String messageID, String url, double baseFeePercent, double arbitrationFeePercent, BigInteger minArbitrationFee)
{
this.uid = uid;
this.name = name;
this.pubKey = pubKey;
this.messageID = messageID;
this.url = url;
this.baseFeePercent = baseFeePercent;
this.arbitrationFeePercent = arbitrationFeePercent;
this.minArbitrationFee = minArbitrationFee;
}
@ -45,4 +55,23 @@ public class Arbitrator implements Serializable
return url;
}
public BigInteger getMinArbitrationFee()
{
return minArbitrationFee;
}
public double getBaseFeePercent()
{
return baseFeePercent;
}
public double getArbitrationFeePercent()
{
return arbitrationFeePercent;
}
public Object getUID()
{
return uid;
}
}

View file

@ -20,6 +20,7 @@ public class User implements Serializable
private List<BankAccount> bankAccounts = new ArrayList<>();
private BankAccount currentBankAccount = null;
public User()
{
}

View file

@ -20,7 +20,12 @@ public class MockData
list.add(Currency.getInstance("JPY"));
list.add(Currency.getInstance("CNY"));
list.add(Currency.getInstance("CHF"));
return randomizeList(list);
return list;
}
public static List<Currency> getRandomCurrencies()
{
return randomizeList(getCurrencies());
}
public static List<Locale> getLocales()
@ -36,20 +41,40 @@ public class MockData
list.add(new Locale("en", "AU"));
list.add(new Locale("it", "IT"));
list.add(new Locale("en", "CA"));
return randomizeList(list);
return list;
}
public static List<Locale> getRandomLocales()
{
return randomizeList(getLocales());
}
public static List<Arbitrator> getArbitrators()
{
List<Arbitrator> list = new ArrayList<>();
list.add(new Arbitrator("Charly Shen", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Shen"));
list.add(new Arbitrator("Tom Shang", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang"));
list.add(new Arbitrator("Edward Swow", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow"));
list.add(new Arbitrator("Julian Sangre", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sangre"));
return randomizeList(list);
list.add(new Arbitrator("uid_1", "Charlie Boom", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom", 0.1, 10, com.google.bitcoin.core.Utils.toNanoCoins("0.01")));
list.add(new Arbitrator("uid_2", "Tom Shang", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang", 0, 1, com.google.bitcoin.core.Utils.toNanoCoins("0.001")));
list.add(new Arbitrator("uid_3", "Edward Snow", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow", 0.2, 5, com.google.bitcoin.core.Utils.toNanoCoins("0.05")));
list.add(new Arbitrator("uid_4", "Julian Sander", UUID.randomUUID().toString(),
UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander", 0, 20, com.google.bitcoin.core.Utils.toNanoCoins("0.1")));
return list;
}
public static List<Arbitrator> getRandomArbitrators()
{
return randomizeList(getArbitrators());
}
public static List<BankAccountType.BankAccountTypeEnum> getBankTransferTypeEnums()
{
return Utils.getAllBankAccountTypeEnums();
}
public static List<BankAccountType.BankAccountTypeEnum> getRandomBankTransferTypeEnums()
{
return randomizeList(Utils.getAllBankAccountTypeEnums());
}

View file

@ -3,6 +3,7 @@ package io.bitsquare.util;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import io.bitsquare.bank.BankAccountType;
import javafx.animation.AnimationTimer;
import org.slf4j.Logger;
@ -20,12 +21,66 @@ public class Utils
{
private static final Logger log = LoggerFactory.getLogger(Utils.class);
public static String convertToJson(Object object)
public static String objectToJson(Object object)
{
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
return gson.toJson(object);
}
public static <T> T jsonToObject(String jsonString, Class<T> classOfT)
{
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
return gson.fromJson(jsonString, classOfT);
}
public static Object deserializeHexStringToObject(String serializedHexString)
{
Object result = null;
try
{
ByteInputStream byteInputStream = new ByteInputStream();
byteInputStream.setBuf(com.google.bitcoin.core.Utils.parseAsHexOrBase58(serializedHexString));
ObjectInputStream objectInputStream = new ObjectInputStream(byteInputStream);
try
{
result = objectInputStream.readObject();
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} finally
{
byteInputStream.close();
objectInputStream.close();
}
} catch (IOException i)
{
i.printStackTrace();
}
return result;
}
public static String serializeObjectToHexString(Object serializable)
{
String result = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try
{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(serializable);
result = com.google.bitcoin.core.Utils.bytesToHexString(byteArrayOutputStream.toByteArray());
byteArrayOutputStream.close();
objectOutputStream.close();
} catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return result;
}
private static long lastTimeStamp = System.currentTimeMillis();
public static void printElapsedTime(String msg)

View file

@ -15,8 +15,9 @@
<logger name="io.bitsquare" level="DEBUG"/>
<logger name="com.google.bitcoin" level="WARN"/>
<logger name="com.google.bitcoin" level="INFO"/>
<logger name="com.google.bitcoin.core.Peer" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerGroup" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.net.NioClientManager" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.net.ConnectionHandler" level="ERROR" additivity="false"/>