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 * bitcoinj integration
* Setup with account registration and tx with OP_RETURN + embedded and blinded bank account data * 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 * Offer fee payment with a OP_RETURN tx and fees to miners
* Pay in to MS fund
### Next steps: ### Next steps:
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer) * Payout from MS fund
* Arbitrator integration * Arbitrator integration
* Messaging system * 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 - pay out from MS in payment process
- btc payments in payment process
- arbitration integration - arbitration integration
Messaging! Messaging!
@ -9,8 +8,7 @@ low prio:
- add settings after setup - add settings after setup
- settings screen - settings screen
- return to setup when unregistered, change/add bank accounts from settings - 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; package io.bitsquare;
import com.google.bitcoin.core.Utils;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import io.bitsquare.bank.BankAccount; import io.bitsquare.bank.BankAccount;
@ -45,9 +46,11 @@ public class BitSquare extends Application
final Storage storage = injector.getInstance(Storage.class); final Storage storage = injector.getInstance(Storage.class);
user.updateFromStorage((User) storage.read(user.getClass().getName())); user.updateFromStorage((User) storage.read(user.getClass().getName()));
settings.updateFromStorage((Settings) storage.read(settings.getClass().getName())); // mock
initSettings(settings, storage, user); initSettings(settings, storage, user);
settings.updateFromStorage((Settings) storage.read(settings.getClass().getName()));
stage.setTitle("BitSquare"); stage.setTitle("BitSquare");
GuiceFXMLLoader.setInjector(injector); GuiceFXMLLoader.setInjector(injector);
@ -83,55 +86,62 @@ public class BitSquare extends Application
{ {
// write default settings // write default settings
settings.getAcceptedCountryLocales().clear(); settings.getAcceptedCountryLocales().clear();
settings.getAcceptedLanguageLocales().clear(); // settings.addAcceptedLanguageLocale(Locale.getDefault());
settings.addAcceptedLanguageLocale(MockData.getLocales().get(0));
settings.addAcceptedLanguageLocale(Locale.getDefault());
settings.addAcceptedCountryLocale(Locale.getDefault());
//TODO mock
settings.addAcceptedLanguageLocale(new Locale("en", "US")); settings.addAcceptedLanguageLocale(new Locale("en", "US"));
settings.addAcceptedLanguageLocale(new Locale("es", "ES")); 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("en", "US"));
settings.addAcceptedCountryLocale(new Locale("es", "ES")); 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.getAcceptedArbitrators().clear();
settings.addArbitrator(new Arbitrator("Tom Shang", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang")); settings.addAcceptedArbitrator(new Arbitrator("uid_1", "Charlie Boom", UUID.randomUUID().toString(),
settings.addArbitrator(new Arbitrator("Edward Snow", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow")); UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom", 0.1, 10, Utils.toNanoCoins("0.01")));
settings.addArbitrator(new Arbitrator("Julian Sander", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander")); 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); storage.write(settings.getClass().getName(), settings);
initMockUser(storage, user); //initMockUser(storage, user);
}
else
{
settings.updateFromStorage(savedSettings);
} }
} }
private void initMockUser(Storage storage, User user) private void initMockUser(Storage storage, User user)
{ {
user.getBankAccounts().clear();
BankAccount bankAccount1 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.SEPA, "Iban", "Bic"), BankAccount bankAccount1 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.SEPA, "Iban", "Bic"),
MockData.getCurrencies().get(0), MockData.getCurrencies().get(0),
MockData.getLocales().get(0), MockData.getLocales().get(0),
"Main account", "Main EUR account",
"Manfred Karrer", "Manfred Karrer",
"564613242346", "564613242346",
"23432432434" "23432432434"
); );
BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.OK_PAY, "Number", "ID"), user.addBankAccount(bankAccount1);
MockData.getCurrencies().get(0),
MockData.getLocales().get(0), BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.INTERNATIONAL, "Number", "ID"),
"OK account", MockData.getCurrencies().get(1),
MockData.getLocales().get(2),
"US account",
"Manfred Karrer", "Manfred Karrer",
"22312123123123123", "22312123123123123",
"asdasdasdas" "asdasdasdas"
); );
user.addBankAccount(bankAccount2); user.addBankAccount(bankAccount2);
user.addBankAccount(bankAccount1);
user.setAccountID(UUID.randomUUID().toString()); user.setAccountID(UUID.randomUUID().toString());
storage.write(user.getClass().getName(), user); 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"); walletFile = new File(".", "bitsquare_account_reg" + ".wallet");
if (walletFile.exists()) if (walletFile.exists())
{ {
FileInputStream walletStream = null;
try try
{ {
FileInputStream walletStream = new FileInputStream(walletFile); walletStream = new FileInputStream(walletFile);
new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this);
} catch (FileNotFoundException e) } catch (FileNotFoundException e)
{ {
e.printStackTrace(); e.printStackTrace();
} catch (UnreadableWalletException e) }
try
{ {
e.printStackTrace(); new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this);
} catch (IOException e) } catch (UnreadableWalletException | IOException e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
} }
else else
{ {
@ -69,7 +72,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
saveToFile(walletFile); saveToFile(walletFile);
} catch (IOException e) } catch (IOException e)
{ {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. e.printStackTrace();
} }
autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null); autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null);
} }
@ -81,7 +84,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
saveToFile(walletFile); saveToFile(walletFile);
} catch (IOException e) } 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; 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.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Locale;
public class BtcFormatter public class BtcFormatter
{ {
@ -12,4 +18,31 @@ public class BtcFormatter
{ {
return satoshis.doubleValue() / BTC.doubleValue(); 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; package io.bitsquare.btc;
import com.google.bitcoin.core.Transaction;
import java.math.BigInteger; import java.math.BigInteger;
public class Fees public class Fees
{ {
// 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 = Transaction.MIN_NONDUST_OUTPUT;// Utils.toNanoCoins("0.001"); public static BigInteger ACCOUNT_REGISTRATION_FEE = BigInteger.valueOf(7860);// Utils.toNanoCoins("0.001");
public static BigInteger OFFER_CREATION_FEE = Transaction.MIN_NONDUST_OUTPUT; // 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 = OFFER_CREATION_FEE; public static BigInteger OFFER_TAKER_FEE = BigInteger.valueOf(7860);
} }

View file

@ -1,16 +1,20 @@
package io.bitsquare.btc; package io.bitsquare.btc;
import com.google.bitcoin.core.*; import com.google.bitcoin.core.*;
import com.google.bitcoin.crypto.TransactionSignature;
import com.google.bitcoin.kits.WalletAppKit; import com.google.bitcoin.kits.WalletAppKit;
import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.RegTestParams; import com.google.bitcoin.params.RegTestParams;
import com.google.bitcoin.script.Script; import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder; import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.utils.Threading; 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.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.crypto.CryptoFacade; import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.gui.util.Popups;
import javafx.application.Platform; import javafx.application.Platform;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,9 +23,7 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.concurrent.ExecutionException;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
/** /**
* That facade delivers wallet functionality from the bitcoinJ library * 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 MAIN_NET = "MAIN_NET";
public static final String TEST_NET = "TEST_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 static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
private NetworkParameters networkParameters; private NetworkParameters params;
private WalletAppKit walletAppKit; private WalletAppKit walletAppKit;
private CryptoFacade cryptoFacade; private CryptoFacade cryptoFacade;
private BlockChainFacade blockChainFacade;
// that wallet is used only for the registration process // that wallet is used only for the registration process
private AccountRegistrationWallet accountRegistrationWallet = null; private AccountRegistrationWallet accountRegistrationWallet = null;
private List<DownloadListener> downloadListeners = new ArrayList<>(); private List<DownloadListener> downloadListeners = new ArrayList<>();
private List<WalletListener> walletListeners = new ArrayList<>(); private List<WalletListener> walletListeners = new ArrayList<>();
private Wallet wallet;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject @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.walletAppKit = walletAppKit;
this.cryptoFacade = cryptoFacade; this.cryptoFacade = cryptoFacade;
this.blockChainFacade = blockChainFacade;
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void initWallet() public void initWallet()
{ {
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means // 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. // a future version.
Threading.USER_THREAD = Platform::runLater; Threading.USER_THREAD = Platform::runLater;
if (networkParameters == RegTestParams.get()) if (params == RegTestParams.get())
{ {
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally. 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 // 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. // 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. // or progress widget to keep the user engaged whilst we initialise, but we don't.
walletAppKit.setDownloadListener(new BlockChainDownloadListener()) walletAppKit.setDownloadListener(new BlockChainDownloadListener())
.setBlockingStartup(false) .setBlockingStartup(false)
.setUserAgent("BitSquare", "1.0"); .setUserAgent("BitSquare", "0.1");
walletAppKit.startAsync(); walletAppKit.startAsync();
walletAppKit.awaitRunning(); 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! // Don't make the user wait for confirmations for now, as the intention is they're sending it their own money!
getWallet().allowSpendingUnconfirmedTransactions(); wallet.allowSpendingUnconfirmedTransactions();
walletAppKit.peerGroup().setMaxConnections(11); 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() public void shutDown()
@ -100,6 +316,10 @@ public class WalletFacade implements WalletEventListener
walletAppKit.awaitTerminated(); walletAppKit.awaitTerminated();
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Listener
///////////////////////////////////////////////////////////////////////////////////////////
public void addDownloadListener(DownloadListener listener) public void addDownloadListener(DownloadListener listener)
{ {
downloadListeners.add(listener); downloadListeners.add(listener);
@ -131,23 +351,40 @@ public class WalletFacade implements WalletEventListener
} }
//MOCK ///////////////////////////////////////////////////////////////////////////////////////////
public KeyPair createNewAddress() // Trading wallet
{ ///////////////////////////////////////////////////////////////////////////////////////////
return new KeyPair(UUID.randomUUID().toString(), UUID.randomUUID().toString());
}
public BigInteger getBalance() public BigInteger getBalance()
{ {
return getWallet().getBalance(Wallet.BalanceType.ESTIMATED); return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
} }
public String getAddress() 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() public Address getAccountRegistrationAddress()
{ {
return getAccountRegistrationWallet().getAddress(); return getAccountRegistrationWallet().getAddress();
@ -155,7 +392,6 @@ public class WalletFacade implements WalletEventListener
public String getAccountRegistrationPubKey() public String getAccountRegistrationPubKey()
{ {
return Utils.bytesToHexString(getAccountRegistrationWallet().getKey().getPubKey()); return Utils.bytesToHexString(getAccountRegistrationWallet().getKey().getPubKey());
} }
@ -169,14 +405,9 @@ public class WalletFacade implements WalletEventListener
getAccountRegistrationWallet().saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(getAccountRegistrationWallet().getKey(), stringifiedBankAccounts)); getAccountRegistrationWallet().saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(getAccountRegistrationWallet().getKey(), stringifiedBankAccounts));
} }
public boolean verifyAccountRegistration(String address, String hashAsHexStringToVerify, byte[] pubKey, String bankAccountIDs, String signatureBankAccountIDs) ///////////////////////////////////////////////////////////////////////////////////////////
{ // Getter
return true; ///////////////////////////////////////////////////////////////////////////////////////////
/*
return cryptoFacade.verifySignature(pubKey, bankAccountIDs, signatureBankAccountIDs)
&& cryptoFacade.verifyHash(hashAsHexStringToVerify, bankAccountIDs, signatureBankAccountIDs)
&& blockChainFacade.verifyEmbeddedData(address); */
}
public int getRegConfNumBroadcastPeers() public int getRegConfNumBroadcastPeers()
{ {
@ -188,7 +419,16 @@ public class WalletFacade implements WalletEventListener
return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet()); return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet());
} }
// WalletEventListener public ECKey getAccountKey()
{
return getAccountRegistrationWallet().getKey();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletEventListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) 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) public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{ {
for (WalletListener walletListener : walletListeners) 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()); log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
} }
@ -238,61 +478,33 @@ public class WalletFacade implements WalletEventListener
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private AccountRegistrationWallet getAccountRegistrationWallet() private AccountRegistrationWallet getAccountRegistrationWallet()
{ {
if (accountRegistrationWallet == null) if (accountRegistrationWallet == null)
accountRegistrationWallet = new AccountRegistrationWallet(networkParameters, walletAppKit.chain(), walletAppKit.peerGroup()); accountRegistrationWallet = new AccountRegistrationWallet(params, walletAppKit.chain(), walletAppKit.peerGroup());
return accountRegistrationWallet; 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() List<ECKey> keys = ImmutableList.of(offererKey, takerKey, arbitratorKey);
.op(OP_RETURN) return ScriptBuilder.createMultiSigOutputScript(2, keys);
.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(); ///////////////////////////////////////////////////////////////////////////////////////////
} // Inner classes
///////////////////////////////////////////////////////////////////////////////////////////
private Wallet getWallet()
{
return walletAppKit.wallet();
}
// inner classes
private class BlockChainDownloadListener extends com.google.bitcoin.core.DownloadListener private class BlockChainDownloadListener extends com.google.bitcoin.core.DownloadListener
{ {
@Override @Override
@ -326,5 +538,4 @@ public class WalletFacade implements WalletEventListener
void onCoinsReceived(BigInteger newBalance); 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.Transaction;
import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.TransactionConfidence;
import com.google.bitcoin.core.Wallet; import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Set; import java.util.Set;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
public class WalletUtil public class WalletUtil
{ {
@ -41,4 +45,11 @@ public class WalletUtil
} }
return null; 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)); return Utils.sha256hash160(concatenateChunks(stringifiedBankAccounts, signedBankAccountIDs).getBytes(Charsets.UTF_8));
} }
public String signContract(ECKey key, String contractAsJson)
// TODO MOCK
public String signContract(String contractAsJson)
{ {
return contractAsJson; return key.signMessage(contractAsJson);
} }
// registration // registration

View file

@ -62,7 +62,7 @@ class WalletAppKitProvider implements Provider<WalletAppKit>
public WalletAppKit get() 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 com.google.inject.Inject;
import io.bitsquare.bank.BankAccount; import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.di.GuiceFXMLLoader; import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.components.NetworkSyncPane; import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.market.MarketController; import io.bitsquare.gui.market.MarketController;
import io.bitsquare.gui.setup.SetupController; import io.bitsquare.gui.setup.SetupController;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.gui.util.Icons; import io.bitsquare.gui.util.Icons;
import io.bitsquare.gui.util.Localisation; import io.bitsquare.gui.util.Localisation;
import io.bitsquare.trade.Direction; import io.bitsquare.trade.Direction;
@ -251,7 +251,7 @@ public class MainController implements Initializable, NavigationController, Wall
balanceLabel.setMouseTransparent(true); balanceLabel.setMouseTransparent(true);
balanceLabel.setPrefWidth(90); balanceLabel.setPrefWidth(90);
balanceLabel.setId("nav-balance-label"); balanceLabel.setId("nav-balance-label");
balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getBalance(), false)); balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
Label balanceCurrencyLabel = new Label("BTC"); Label balanceCurrencyLabel = new Label("BTC");
balanceCurrencyLabel.setPadding(new Insets(6, 0, 0, 0)); 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 com.google.inject.Inject;
import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ChildController; import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.util.Formatter;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Label; import javafx.scene.control.Label;
@ -63,7 +63,7 @@ public class FundsController implements Initializable, ChildController
this.navigationController = navigationController; this.navigationController = navigationController;
addressLabel.setText(walletFacade.getAddress()); 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; package io.bitsquare.gui.market.offer;
import com.google.bitcoin.core.InsufficientMoneyException; 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 com.google.inject.Inject;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.Fees; import io.bitsquare.btc.Fees;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.ChildController; import io.bitsquare.gui.ChildController;
@ -13,6 +16,7 @@ import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer; import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trading; import io.bitsquare.trade.Trading;
import io.bitsquare.trade.orderbook.OrderBookFilter; import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@ -22,10 +26,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -43,9 +45,11 @@ public class CreateOfferController implements Initializable, ChildController, Wa
private Settings settings; private Settings settings;
private User user; private User user;
private Direction direction; private Direction direction;
private Offer offer;
private int gridRow;
private Button placeOfferButton; private Button placeOfferButton;
private int gridRow; private TextField collateralTextField, minAmountTextField;
@FXML @FXML
private AnchorPane holderPane; private AnchorPane holderPane;
@ -54,7 +58,12 @@ public class CreateOfferController implements Initializable, ChildController, Wa
@FXML @FXML
public Label buyLabel; public Label buyLabel;
@FXML @FXML
public TextField volume, amount, price, minAmount; public TextField volumeTextField, amountTextField, priceTextField;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public CreateOfferController(Trading trading, WalletFacade walletFacade, Settings settings, User user) public CreateOfferController(Trading trading, WalletFacade walletFacade, Settings settings, User user)
@ -65,86 +74,51 @@ public class CreateOfferController implements Initializable, ChildController, Wa
this.user = user; 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 @Override
public void initialize(URL url, ResourceBundle rb) public void initialize(URL url, ResourceBundle rb)
{ {
walletFacade.addRegistrationWalletListener(this); walletFacade.addRegistrationWalletListener(this);
gridRow = 2; buildScreen();
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); // Interface implementation: ChildController
///////////////////////////////////////////////////////////////////////////////////////////
placeOfferButton = new Button("Place offer");
formGridPane.add(placeOfferButton, 1, ++gridRow);
placeOfferButton.setDefaultButton(true);
// handlers
amount.textProperty().addListener(new ChangeListener<String>()
{
@Override @Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) public void setNavigationController(NavigationController navigationController)
{ {
updateVolume(); this.navigationController = navigationController;
}
});
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();
}
}
});
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletFacade.WalletListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override @Override
public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks) public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks)
{ {
@ -157,11 +131,119 @@ public class CreateOfferController implements Initializable, ChildController, Wa
log.info("onCoinsReceived " + newBalance); 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) private void buildConfirmationView(String txID)
{ {
FormBuilder.addTextField(formGridPane, "Transaction ID:", txID, ++gridRow, false, true); 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"); Button closeButton = new Button("Close");
formGridPane.add(closeButton, 1, ++gridRow); 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() 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() private void updateVolume()
{ {
double amountAsDouble = Converter.stringToDouble(amount.getText()); volumeTextField.setText(Formatter.formatVolume(getVolume()));
double priceAsDouble = Converter.stringToDouble(price.getText());
volume.setText(Formatter.formatPrice(amountAsDouble * priceAsDouble));
} }
private Image getConfirmIconImage(int numBroadcastPeers, int depthInBlocks) private double getVolume()
{ {
if (depthInBlocks > 0) double amountAsDouble = Converter.stringToDouble(amountTextField.getText());
return Icons.getIconImage(Icons.getIconIDForConfirmations(depthInBlocks)); double priceAsDouble = Converter.stringToDouble(priceTextField.getText());
else return amountAsDouble * priceAsDouble;
return Icons.getIconImage(Icons.getIconIDForPeersSeenTx(numBroadcastPeers));
} }
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,11 +6,9 @@
<AnchorPane fx:id="holderPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" <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" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.market.offer.CreateOfferController"> fx:controller="io.bitsquare.gui.market.offer.CreateOfferController">
<children>
<VBox spacing="10" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="5.0" <VBox spacing="10" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="5.0"
AnchorPane.bottomAnchor="10.0"> AnchorPane.bottomAnchor="10.0">
<GridPane fx:id="formGridPane" vgap="5" hgap="5"> <GridPane fx:id="formGridPane" vgap="5" hgap="5">
<children>
<Label id="form-header-text" text="Create new offer:" GridPane.rowIndex="0" <Label id="form-header-text" text="Create new offer:" GridPane.rowIndex="0"
GridPane.columnIndex="0"/> GridPane.columnIndex="0"/>
@ -18,23 +16,15 @@
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" spacing="5" <HBox GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" spacing="5"
alignment="CENTER_RIGHT"> alignment="CENTER_RIGHT">
<children> <TextField fx:id="amountTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<TextField fx:id="amount" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="BTC for:"/> <Label text="BTC for:"/>
<TextField fx:id="price" prefWidth="70.0" alignment="CENTER_RIGHT"/> <TextField fx:id="priceTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="EUR ="/> <Label text="EUR ="/>
<TextField fx:id="volume" prefWidth="70.0" alignment="CENTER_RIGHT" <TextField fx:id="volumeTextField" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"
mouseTransparent="true"/> mouseTransparent="true"/>
<Label text="EUR in total"/> <Label text="EUR in total"/>
</children>
</HBox> </HBox>
<Label text="Min. Amount:" GridPane.rowIndex="2" GridPane.columnIndex="0"/>
<TextField fx:id="minAmount" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</children>
<padding> <padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
</padding> </padding>
@ -46,5 +36,4 @@
</columnConstraints> </columnConstraints>
</GridPane> </GridPane>
</VBox> </VBox>
</children>
</AnchorPane> </AnchorPane>

View file

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

View file

@ -22,7 +22,7 @@ public class OrderBookListItem
this.offer = offer; this.offer = offer;
this.price.set(Formatter.formatPrice(offer.getPrice())); 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())); this.volume.set(Formatter.formatVolumeWithMinVolume(offer.getVolume(), offer.getMinVolume()));
} }

View file

@ -1,5 +1,8 @@
package io.bitsquare.gui.market.trade; 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 com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.BtcFormatter;
@ -23,7 +26,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import org.controlsfx.dialog.Dialogs;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -36,7 +38,7 @@ import java.util.ResourceBundle;
public class TradeController implements Initializable, ChildController, WalletFacade.WalletListener public class TradeController implements Initializable, ChildController, WalletFacade.WalletListener
{ {
private static final Logger log = LoggerFactory.getLogger(TradeController.class); 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 Trading trading;
private WalletFacade walletFacade; private WalletFacade walletFacade;
@ -44,14 +46,14 @@ public class TradeController implements Initializable, ChildController, WalletFa
private Offer offer; private Offer offer;
private Trade trade; private Trade trade;
private Contract contract; private Contract contract;
private double requestedAmount; private BigInteger requestedAmount;
private boolean offererIsOnline; private boolean offererIsOnline;
private int row; private int row;
private List<ProcessStepItem> processStepItems = new ArrayList(); private List<ProcessStepItem> processStepItems = new ArrayList();
private NavigationController navigationController; private NavigationController navigationController;
private TextField amountTextField, totalToPayLabel, totalLabel; private TextField amountTextField, totalToPayLabel, totalLabel, collateralTextField;
private Label statusTextField, infoLabel; private Label statusTextField, infoLabel;
private Button nextButton; private Button nextButton;
private ProgressBar progressBar; private ProgressBar progressBar;
@ -81,14 +83,14 @@ public class TradeController implements Initializable, ChildController, WalletFa
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void initWithData(Offer offer, double requestedAmount) public void initWithData(Offer offer, BigInteger requestedAmount)
{ {
this.offer = offer; 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); trade.setTradeAmount(requestedAmount);
contract = trading.createNewContract(trade); contract = trading.createContract(trade);
processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC")); processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC"));
processStepItems.add(new ProcessStepItem("Bank transfer")); processStepItems.add(new ProcessStepItem("Bank transfer"));
@ -148,19 +150,22 @@ public class TradeController implements Initializable, ChildController, WalletFa
row = -1; row = -1;
FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row); 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 -> { amountTextField.textProperty().addListener(e -> {
applyVolume(); applyVolume();
applyCollateral();
totalToPayLabel.setText(getTotalToPay()); 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); gridPane.add(amountRangeLabel, 2, row);
FormBuilder.addTextField(gridPane, "Price:", Formatter.formatPriceWithCurrencyPair(offer.getPrice(), offer.getCurrency()), ++row); FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", Formatter.formatPrice(offer.getPrice()), ++row);
totalLabel = FormBuilder.addTextField(gridPane, "Total:", Formatter.formatVolume(getVolume(), offer.getCurrency()), ++row); totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row);
FormBuilder.addTextField(gridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_TAKER_FEE, true), ++row); collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row);
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay:", getTotalToPay(), ++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 = FormBuilder.addButton(gridPane, "Take offer and pay", ++row);
nextButton.setDefaultButton(true); nextButton.setDefaultButton(true);
@ -247,9 +252,9 @@ public class TradeController implements Initializable, ChildController, WalletFa
progressIndicatorHolder.getChildren().addAll(progressIndicator); progressIndicatorHolder.getChildren().addAll(progressIndicator);
gridPane.add(progressIndicatorHolder, 1, row); gridPane.add(progressIndicatorHolder, 1, row);
trade.setTradeAmount(Converter.stringToDouble(amountTextField.getText())); trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
trading.sendTakeOfferRequest(trade);
trading.sendTakeOfferRequest(trade);
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> { Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
onTakeOfferRequestConfirmed(); onTakeOfferRequestConfirmed();
progressBar.setProgress(1.0 / 3.0); progressBar.setProgress(1.0 / 3.0);
@ -259,7 +264,13 @@ public class TradeController implements Initializable, ChildController, WalletFa
private void onTakeOfferRequestConfirmed() 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."); statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> { Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
@ -269,6 +280,23 @@ public class TradeController implements Initializable, ChildController, WalletFa
}); });
} }
@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() private void onOfferFeePaymentConfirmed()
{ {
trading.requestOffererDetailData(); trading.requestOffererDetailData();
@ -282,14 +310,10 @@ public class TradeController implements Initializable, ChildController, WalletFa
private void onUserDetailsReceived() private void onUserDetailsReceived()
{ {
if (!walletFacade.verifyAccountRegistration(offer.getAccountID(), null, null, null, null)) if (!blockChainFacade.verifyEmbeddedData(offer.getAccountID()))
{ {
Dialogs.create() Popups.openErrorPopup("Offerers bank account is blacklisted", "Offerers bank account is blacklisted.");
.title("Offerers bank account is blacklisted") return;
.message("Offerers bank account is blacklisted.")
.nativeTitleBar()
.lightweight()
.showError();
} }
trading.signContract(contract); trading.signContract(contract);
@ -337,8 +361,19 @@ public class TradeController implements Initializable, ChildController, WalletFa
gridPane.getChildren().clear(); gridPane.getChildren().clear();
row = -1; row = -1;
FormBuilder.addHeaderLabel(gridPane, "Trade successfully completed", ++row); FormBuilder.addHeaderLabel(gridPane, "Trade successfully completed", ++row);
FormBuilder.addTextField(gridPane, "You have payed:", getTotalToPay(), ++row); FormBuilder.addTextField(gridPane, "You have payed in total (BTC):", getTotalToPay(), ++row);
FormBuilder.addTextField(gridPane, "You have received:", getTotalToReceive(), ++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); gridPane.add(nextButton, 1, ++row);
} }
@ -355,8 +390,8 @@ public class TradeController implements Initializable, ChildController, WalletFa
// Other Private methods // Other Private methods
private boolean tradeAmountValid() private boolean tradeAmountValid()
{ {
double tradeAmount = Converter.stringToDouble(amountTextField.getText()); BigInteger tradeAmount = BtcFormatter.stringValueToSatoshis(amountTextField.getText());
return tradeAmount <= offer.getAmount() && tradeAmount >= offer.getMinAmount(); return tradeAmount.compareTo(offer.getAmount()) <= 0 && tradeAmount.compareTo(offer.getMinAmount()) >= 0;
} }
private boolean takerIsSelling() private boolean takerIsSelling()
@ -388,28 +423,31 @@ public class TradeController implements Initializable, ChildController, WalletFa
private String getTotalToPay() private String getTotalToPay()
{ {
String result = "";
if (takerIsSelling()) if (takerIsSelling())
{ {
double btcValue = Converter.stringToDouble(amountTextField.getText()) + BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE)/* + return BtcFormatter.formatSatoshis(getAmountInSatoshis().add(Fees.OFFER_TAKER_FEE).add(getCollateralInSatoshis()), false);
offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/;
result = Formatter.formatAmount(btcValue, true, true);
} }
else else
{ {
double btcValue = BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE) /*+ offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/; return BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE.add(getCollateralInSatoshis()), false) + "\n" +
result = Formatter.formatAmount(btcValue, true, true) + "\n" + Formatter.formatVolume(getVolume(), offer.getCurrency()); Formatter.formatVolume(getVolume(), offer.getCurrency());
} }
return result;
} }
private String getTotalToReceive()
private void applyCollateral()
{ {
if (takerIsSelling()) collateralTextField.setText(BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false));
return Formatter.formatVolume(getVolume(), offer.getCurrency());
else
return Formatter.formatAmount(offer.getAmount(), true, true);
} }
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(); ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Task(expectedMsg)); Future<String> future = executor.submit(new Task(expectedMsg));
try
{
try
{
// max timeout 5 sec // max timeout 5 sec
try
{
return future.get(5, TimeUnit.SECONDS); return future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException e) } catch (InterruptedException | TimeoutException | ExecutionException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{ {
e.printStackTrace(); e.printStackTrace();
} }
} catch (TimeoutException e)
{
System.out.println("Terminated!");
}
executor.shutdownNow(); executor.shutdownNow();
return null; return null;
} }

View file

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

View file

@ -7,7 +7,6 @@
xmlns:fx="http://javafx.com/fxml"> xmlns:fx="http://javafx.com/fxml">
<ScrollPane fitToWidth="true" AnchorPane.leftAnchor="10" AnchorPane.rightAnchor="10" AnchorPane.topAnchor="10" <ScrollPane fitToWidth="true" AnchorPane.leftAnchor="10" AnchorPane.rightAnchor="10" AnchorPane.topAnchor="10"
AnchorPane.bottomAnchor="30"> AnchorPane.bottomAnchor="30">
<content>
<VBox fx:id="vBox" spacing="10"> <VBox fx:id="vBox" spacing="10">
<Label text="Setup trading account" id="headline-label"/> <Label text="Setup trading account" id="headline-label"/>
<ProcessStepBar fx:id="processStepBar"/> <ProcessStepBar fx:id="processStepBar"/>
@ -26,6 +25,5 @@
<Button fx:id="nextButton" defaultButton="true"/> <Button fx:id="nextButton" defaultButton="true"/>
<Button fx:id="skipButton"/> <Button fx:id="skipButton"/>
</VBox> </VBox>
</content>
</ScrollPane> </ScrollPane>
</AnchorPane> </AnchorPane>

View file

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

View file

@ -1,5 +1,6 @@
package io.bitsquare.gui.util; package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.components.VSpacer; import io.bitsquare.gui.components.VSpacer;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -8,7 +9,9 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import java.util.Currency;
import java.util.List; import java.util.List;
import java.util.Locale;
public class FormBuilder public class FormBuilder
{ {
@ -67,14 +70,38 @@ public class FormBuilder
return button; 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); 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); gridPane.add(comboBox, 1, row);
return comboBox; 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) public static TextField addConfirmationsLabel(GridPane gridPane, WalletFacade walletFacade, int row)
{ {

View file

@ -1,9 +1,7 @@
package io.bitsquare.gui.util; package io.bitsquare.gui.util;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.trade.Direction; import io.bitsquare.trade.Direction;
import java.math.BigInteger;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.Currency; import java.util.Currency;
import java.util.List; import java.util.List;
@ -88,14 +86,6 @@ public class Formatter
return s.substring(1, s.length() - 1); 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) public static String formatDouble(double value)
{ {
return formatDouble(value, 2); return formatDouble(value, 2);

View file

@ -13,4 +13,14 @@ public class Popups
.lightweight() .lightweight()
.showError(); .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 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) 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) 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.setFromValue(0.0);
ft.setToValue(1.0); ft.setToValue(1.0);
ft.play(); ft.play();
@ -29,7 +29,7 @@ public class Transitions
public static Animation fadeOut(Node ui) 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.setFromValue(ui.getOpacity());
ft.setToValue(0.0); ft.setToValue(0.0);
ft.play(); ft.play();
@ -45,7 +45,7 @@ public class Transitions
public static void blurOut(Node node) 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) public static void blurOut(Node node, int time)
@ -64,7 +64,7 @@ public class Transitions
GaussianBlur blur = (GaussianBlur) node.getEffect(); GaussianBlur blur = (GaussianBlur) node.getEffect();
Timeline timeline = new Timeline(); Timeline timeline = new Timeline();
KeyValue kv = new KeyValue(blur.radiusProperty(), 0.0); 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.getKeyFrames().add(kf);
timeline.setOnFinished(actionEvent -> node.setEffect(null)); timeline.setOnFinished(actionEvent -> node.setEffect(null));
timeline.play(); timeline.play();

View file

@ -49,7 +49,7 @@ public class Message
public String toString() 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 io.bitsquare.user.Arbitrator;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -14,8 +15,9 @@ public class Settings implements Serializable
private List<Locale> acceptedLanguageLocales = new ArrayList<>(); private List<Locale> acceptedLanguageLocales = new ArrayList<>();
private List<Locale> acceptedCountryLocales = 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 // Constructor
@ -37,7 +39,9 @@ public class Settings implements Serializable
{ {
acceptedLanguageLocales = savedSettings.getAcceptedLanguageLocales(); acceptedLanguageLocales = savedSettings.getAcceptedLanguageLocales();
acceptedCountryLocales = savedSettings.getAcceptedCountryLocales(); 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); acceptedCountryLocales.add(locale);
} }
public void addArbitrator(Arbitrator arbitrator) public void addAcceptedArbitrator(Arbitrator arbitrator)
{ {
if (!arbitrators.contains(arbitrator)) if (!acceptedArbitrators.contains(arbitrator))
arbitrators.add(arbitrator); acceptedArbitrators.add(arbitrator);
} }
public void setMaxCollateral(double maxCollateral)
{
this.maxCollateral = maxCollateral;
}
public void setMinCollateral(double minCollateral)
{
this.minCollateral = minCollateral;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public List<Arbitrator> getArbitrators() public List<Arbitrator> getAcceptedArbitrators()
{ {
return arbitrators; return acceptedArbitrators;
} }
public List<Locale> getAcceptedLanguageLocales() public List<Locale> getAcceptedLanguageLocales()
@ -79,8 +92,28 @@ public class Settings implements Serializable
return acceptedCountryLocales; 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.bank.BankAccountType;
import io.bitsquare.user.Arbitrator; import io.bitsquare.user.Arbitrator;
import java.math.BigInteger;
import java.util.Currency; import java.util.Currency;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -12,29 +13,37 @@ public class Offer
{ {
private UUID uid; private UUID uid;
private double price; private double price;
private double amount; private BigInteger amount;
private double minAmount; private BigInteger minAmount;
private String accountID; private String accountID;
private String messageID; private String messageID;
private Direction direction; private Direction direction;
private BankAccountType.BankAccountTypeEnum bankAccountTypeEnum; private BankAccountType.BankAccountTypeEnum bankAccountTypeEnum;
private Currency currency; private Currency currency;
private Locale bankAccountCountryLocale; private Locale bankAccountCountryLocale;
private double collateral;
private List<Locale> acceptedCountryLocales; private List<Locale> acceptedCountryLocales;
private List<Locale> acceptedLanguageLocales; private List<Locale> acceptedLanguageLocales;
private String offerPaymentTxID; private String offerPaymentTxID;
private Arbitrator arbitrator; private Arbitrator arbitrator;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Offer(String accountID, public Offer(String accountID,
String messageID, String messageID,
Direction direction, Direction direction,
double price, double price,
double amount, BigInteger amount,
double minAmount, BigInteger minAmount,
BankAccountType.BankAccountTypeEnum bankAccountTypeEnum, BankAccountType.BankAccountTypeEnum bankAccountTypeEnum,
Currency currency, Currency currency,
Locale bankAccountCountryLocale, Locale bankAccountCountryLocale,
Arbitrator arbitrator, Arbitrator arbitrator,
double collateral,
List<Locale> acceptedCountryLocales, List<Locale> acceptedCountryLocales,
List<Locale> acceptedLanguageLocales) List<Locale> acceptedLanguageLocales)
{ {
@ -48,6 +57,7 @@ public class Offer
this.currency = currency; this.currency = currency;
this.bankAccountCountryLocale = bankAccountCountryLocale; this.bankAccountCountryLocale = bankAccountCountryLocale;
this.arbitrator = arbitrator; this.arbitrator = arbitrator;
this.collateral = collateral;
this.acceptedCountryLocales = acceptedCountryLocales; this.acceptedCountryLocales = acceptedCountryLocales;
this.acceptedLanguageLocales = acceptedLanguageLocales; this.acceptedLanguageLocales = acceptedLanguageLocales;
@ -89,12 +99,12 @@ public class Offer
return price; return price;
} }
public double getAmount() public BigInteger getAmount()
{ {
return amount; return amount;
} }
public double getMinAmount() public BigInteger getMinAmount()
{ {
return minAmount; return minAmount;
} }
@ -131,12 +141,12 @@ public class Offer
public double getVolume() public double getVolume()
{ {
return price * amount; return price * amount.doubleValue();
} }
public double getMinVolume() public double getMinVolume()
{ {
return price * minAmount; return price * minAmount.doubleValue();
} }
public String getOfferPaymentTxID() public String getOfferPaymentTxID()
@ -148,4 +158,9 @@ public class Offer
{ {
return arbitrator; return arbitrator;
} }
public double getCollateral()
{
return collateral;
}
} }

View file

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

View file

@ -1,8 +1,11 @@
package io.bitsquare.trade; package io.bitsquare.trade;
import com.google.bitcoin.core.InsufficientMoneyException; 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 com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.Fees;
import io.bitsquare.btc.KeyPair; import io.bitsquare.btc.KeyPair;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade; import io.bitsquare.crypto.CryptoFacade;
@ -18,8 +21,9 @@ import java.util.HashMap;
import java.util.UUID; 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 public class Trading
{ {
private static final Logger log = LoggerFactory.getLogger(Trading.class); private static final Logger log = LoggerFactory.getLogger(Trading.class);
@ -34,6 +38,11 @@ public class Trading
private CryptoFacade cryptoFacade; private CryptoFacade cryptoFacade;
private Settings settings; private Settings settings;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public Trading(User user, public Trading(User user,
Settings settings, Settings settings,
@ -50,64 +59,21 @@ public class Trading
this.cryptoFacade = cryptoFacade; this.cryptoFacade = cryptoFacade;
} }
/**
* @param offer ///////////////////////////////////////////////////////////////////////////////////////////
*/ // Public Methods
public String placeNewOffer(Offer offer) throws InsufficientMoneyException ///////////////////////////////////////////////////////////////////////////////////////////
public void placeNewOffer(Offer offer, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{ {
log.info("place New Offer"); log.info("place New Offer");
offers.put(offer.getUid().toString(), offer); offers.put(offer.getUid().toString(), offer);
String txID = walletFacade.payOfferFee(); walletFacade.payFee(Fees.OFFER_CREATION_FEE, callback);
offer.setOfferPaymentTxID(txID);
messageFacade.broadcast(new Message(Message.BROADCAST_NEW_OFFER, offer)); messageFacade.broadcast(new Message(Message.BROADCAST_NEW_OFFER, offer));
return txID;
} }
/** public Trade createTrade(Offer offer)
* 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)
{ {
log.info("create New Trade"); log.info("create New Trade");
Trade trade = new Trade(offer); Trade trade = new Trade(offer);
@ -115,55 +81,63 @@ public class Trading
return trade; return trade;
} }
public Contract createContract(Trade trade)
public HashMap<String, Trade> getTrades()
{ {
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 // trade process
*/ // 1
public void payOfferFee(Trade 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());
}
// 2
public void payOfferFee(Trade trade, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{ {
log.info("Pay offer fee"); 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()); // messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
} }
// 3
public void requestOffererDetailData() public void requestOffererDetailData()
{ {
log.info("Request offerer detail data"); log.info("Request offerer detail data");
} }
/** // 4
* @param trade 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) public void payToDepositTx(Trade trade)
{ {
log.info("create MultiSig address"); //walletFacade.takerAddPaymentAndSign();
log.info("Create deposit tx");
log.info("Sign deposit tx");
log.info("Send deposit Tx");
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID()); // messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
} }
// 6
/**
* @param trade
*/
public void releaseBTC(Trade trade) public void releaseBTC(Trade trade)
{ {
log.info("Sign payment tx"); 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()); // 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; package io.bitsquare.trade.orderbook;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.gui.market.orderbook.OrderBookListItem; import io.bitsquare.gui.market.orderbook.OrderBookListItem;
import io.bitsquare.gui.util.Converter; import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter; import io.bitsquare.gui.util.Formatter;
@ -42,19 +43,20 @@ public class MockOrderBook extends OrderBook
direction = Direction.SELL; direction = Direction.SELL;
price = 500 - Math.random() * 50; price = 500 - Math.random() * 50;
} }
double collateral = 0.1;// Math.random() * 20 + 0.1;
Offer offer = new Offer("mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj", Offer offer = new Offer("mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj",
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
direction, direction,
price, price,
amount, BtcFormatter.doubleValueToSatoshis(amount),
minAmount, BtcFormatter.doubleValueToSatoshis(minAmount),
MockData.getBankTransferTypeEnums().get(0), MockData.getRandomBankTransferTypeEnums().get(0),
MockData.getCurrencies().get(0), MockData.getRandomCurrencies().get(0),
MockData.getLocales().get(0), MockData.getRandomLocales().get(0),
MockData.getArbitrators().get(0), MockData.getRandomArbitrators().get(0),
MockData.getLocales(), collateral,
MockData.getLocales()); MockData.getRandomLocales(),
MockData.getRandomLocales());
return offer; return offer;
} }

View file

@ -6,6 +6,7 @@ import io.bitsquare.gui.market.orderbook.OrderBookListItem;
import io.bitsquare.settings.Settings; import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction; import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer; import io.bitsquare.trade.Offer;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -76,7 +77,7 @@ public class OrderBook
// The requested amount must be lower or equal then the offer amount // The requested amount must be lower or equal then the offer amount
boolean amountResult = true; boolean amountResult = true;
if (orderBookFilter.getAmount() > 0) 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 // The requested trade direction must be opposite of the offerList trade direction
boolean directionResult = !orderBookFilter.getDirection().equals(offer.getDirection()); boolean directionResult = !orderBookFilter.getDirection().equals(offer.getDirection());
@ -91,40 +92,46 @@ public class OrderBook
priceResult = orderBookFilter.getPrice() <= offer.getPrice(); 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 boolean result = currencyResult
&& countryResult && countryResult
&& languageResult && languageResult
&& amountResult && amountResult
&& directionResult && directionResult
&& priceResult; && priceResult
&& arbitratorResult;
/* /*
log.debug("result = " + result + log.debug("result = " + result +
", currencyResult = " + currencyResult + ", currencyResult = " + currencyResult +
", countryResult = " + countryResult + ", countryResult = " + countryResult +
", languageResult = " + languageResult + ", languageResult = " + languageResult +
", bankAccountTypeEnumResult = " + bankAccountTypeEnumResult +
", amountResult = " + amountResult + ", amountResult = " + amountResult +
", directionResult = " + directionResult + ", directionResult = " + directionResult +
", priceResult = " + priceResult ", priceResult = " + priceResult +
", arbitratorResult = " + arbitratorResult
); );
log.debug("currentBankAccount.getCurrency() = " + currentBankAccount.getCurrency() + log.debug("currentBankAccount.getCurrency() = " + currentBankAccount.getCurrency() +
", offer.getCurrency() = " + offer.getCurrency()); ", offer.getCurrency() = " + offer.getCurrency());
log.debug("offer.getCountryLocale() = " + offer.getCountryLocale() + log.debug("offer.getCountryLocale() = " + offer.getBankAccountCountryLocale() +
", settings.getAcceptedCountryLocales() = " + settings.getAcceptedCountryLocales().toString()); ", settings.getAcceptedCountryLocales() = " + settings.getAcceptedCountryLocales().toString());
log.debug("settings.getAcceptedLanguageLocales() = " + settings.getAcceptedLanguageLocales() + log.debug("settings.getAcceptedLanguageLocales() = " + settings.getAcceptedLanguageLocales() +
", constraints.getAcceptedLanguageLocales() = " + constraints.getLanguageLocales()); ", offer.getAcceptedLanguageLocales() = " + offer.getAcceptedLanguageLocales());
log.debug("currentBankAccount.getBankAccountType().getType() = " + currentBankAccount.getBankAccountType().getType() + log.debug("currentBankAccount.getBankAccountType().getType() = " + currentBankAccount.getBankAccountType().getType() +
", constraints.getBankAccountTypes() = " + constraints.getBankAccountTypes()); ", offer.getBankAccountTypeEnum() = " + offer.getBankAccountTypeEnum());
log.debug("orderBookFilter.getAmount() = " + orderBookFilter.getAmount() + log.debug("orderBookFilter.getAmount() = " + orderBookFilter.getAmount() +
", offer.getAmount() = " + offer.getAmount()); ", offer.getAmount() = " + offer.getAmount());
log.debug("orderBookFilter.getDirection() = " + orderBookFilter.getDirection() + log.debug("orderBookFilter.getDirection() = " + orderBookFilter.getDirection() +
", offer.getDirection() = " + offer.getDirection()); ", offer.getDirection() = " + offer.getDirection());
log.debug("orderBookFilter.getPrice() = " + orderBookFilter.getPrice() + log.debug("orderBookFilter.getPrice() = " + orderBookFilter.getPrice() +
", offer.getPrice() = " + offer.getPrice()); ", offer.getPrice() = " + offer.getPrice());
log.debug("offer.getArbitrator() = " + offer.getArbitrator() +
", settings.getAcceptedArbitrators() = " + settings.getAcceptedArbitrators());
*/ */
return result; return result;
} }
}); });
@ -144,26 +151,45 @@ public class OrderBook
// Private Methods // 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 true;
} }
return false; 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 true;
} }
} }
return false; 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; package io.bitsquare.user;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger;
public class Arbitrator implements Serializable public class Arbitrator implements Serializable
{ {
@ -11,13 +12,22 @@ public class Arbitrator implements Serializable
private String messageID; private String messageID;
private String url; 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.name = name;
this.pubKey = pubKey; this.pubKey = pubKey;
this.messageID = messageID; this.messageID = messageID;
this.url = url; this.url = url;
this.baseFeePercent = baseFeePercent;
this.arbitrationFeePercent = arbitrationFeePercent;
this.minArbitrationFee = minArbitrationFee;
} }
@ -45,4 +55,23 @@ public class Arbitrator implements Serializable
return url; 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 List<BankAccount> bankAccounts = new ArrayList<>();
private BankAccount currentBankAccount = null; private BankAccount currentBankAccount = null;
public User() public User()
{ {
} }

View file

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

View file

@ -3,6 +3,7 @@ package io.bitsquare.util;
import com.google.gson.FieldNamingPolicy; import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import io.bitsquare.bank.BankAccountType; import io.bitsquare.bank.BankAccountType;
import javafx.animation.AnimationTimer; import javafx.animation.AnimationTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -20,12 +21,66 @@ public class Utils
{ {
private static final Logger log = LoggerFactory.getLogger(Utils.class); 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(); Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
return gson.toJson(object); 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(); private static long lastTimeStamp = System.currentTimeMillis();
public static void printElapsedTime(String msg) public static void printElapsedTime(String msg)

View file

@ -15,8 +15,9 @@
<logger name="io.bitsquare" level="DEBUG"/> <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.core.PeerGroup" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.net.NioClientManager" 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"/> <logger name="com.google.bitcoin.net.ConnectionHandler" level="ERROR" additivity="false"/>