diff --git a/README.md b/README.md index 249d3d4b83..b85f5d5494 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,13 @@ git clone --recursive git://github.com/bitsquare/bitsquare * bitcoinj integration * Setup with account registration and tx with OP_RETURN + embedded and blinded bank account data * Offer fee payment with a OP_RETURN tx and fees to miners +* Pay in to MS fund ### Next steps: -* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer) +* Payout from MS fund * Arbitrator integration * Messaging system +* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer) * ... diff --git a/TODO.txt b/TODO.txt index 30d215a34e..2f9cc5dcbf 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,5 +1,4 @@ -- payment process update with new models -- btc payments in payment process +- pay out from MS in payment process - arbitration integration Messaging! @@ -9,8 +8,7 @@ low prio: - add settings after setup - settings screen - return to setup when unregistered, change/add bank accounts from settings -- warning popups -- +- BigInteger for all btc values diff --git a/src/main/java/io/bitsquare/BitSquare.java b/src/main/java/io/bitsquare/BitSquare.java index fd5aa9a94f..113cd712e8 100644 --- a/src/main/java/io/bitsquare/BitSquare.java +++ b/src/main/java/io/bitsquare/BitSquare.java @@ -1,5 +1,6 @@ package io.bitsquare; +import com.google.bitcoin.core.Utils; import com.google.inject.Guice; import com.google.inject.Injector; import io.bitsquare.bank.BankAccount; @@ -45,9 +46,11 @@ public class BitSquare extends Application final Storage storage = injector.getInstance(Storage.class); user.updateFromStorage((User) storage.read(user.getClass().getName())); - settings.updateFromStorage((Settings) storage.read(settings.getClass().getName())); + // mock initSettings(settings, storage, user); + settings.updateFromStorage((Settings) storage.read(settings.getClass().getName())); + stage.setTitle("BitSquare"); GuiceFXMLLoader.setInjector(injector); @@ -83,55 +86,62 @@ public class BitSquare extends Application { // write default settings settings.getAcceptedCountryLocales().clear(); - settings.getAcceptedLanguageLocales().clear(); - - settings.addAcceptedLanguageLocale(Locale.getDefault()); - settings.addAcceptedCountryLocale(Locale.getDefault()); - - //TODO mock + // settings.addAcceptedLanguageLocale(Locale.getDefault()); + settings.addAcceptedLanguageLocale(MockData.getLocales().get(0)); settings.addAcceptedLanguageLocale(new Locale("en", "US")); settings.addAcceptedLanguageLocale(new Locale("es", "ES")); - settings.addAcceptedCountryLocale(new Locale("de", "AT")); + settings.getAcceptedCountryLocales().clear(); + //settings.addAcceptedCountryLocale(Locale.getDefault()); + settings.addAcceptedCountryLocale(MockData.getLocales().get(0)); settings.addAcceptedCountryLocale(new Locale("en", "US")); settings.addAcceptedCountryLocale(new Locale("es", "ES")); - settings.addArbitrator(new Arbitrator("Charly Boom", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom")); - settings.addArbitrator(new Arbitrator("Tom Shang", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang")); - settings.addArbitrator(new Arbitrator("Edward Snow", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow")); - settings.addArbitrator(new Arbitrator("Julian Sander", UUID.randomUUID().toString(), UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander")); + settings.getAcceptedArbitrators().clear(); + settings.addAcceptedArbitrator(new Arbitrator("uid_1", "Charlie Boom", UUID.randomUUID().toString(), + UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom", 0.1, 10, Utils.toNanoCoins("0.01"))); + settings.addAcceptedArbitrator(new Arbitrator("uid_2", "Tom Shang", UUID.randomUUID().toString(), + UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang", 0, 1, Utils.toNanoCoins("0.001"))); + settings.addAcceptedArbitrator(new Arbitrator("uid_3", "Edward Snow", UUID.randomUUID().toString(), + UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow", 0.2, 5, Utils.toNanoCoins("0.05"))); + settings.addAcceptedArbitrator(new Arbitrator("uid_4", "Julian Sander", UUID.randomUUID().toString(), + UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander", 0, 20, Utils.toNanoCoins("0.1"))); + + settings.setMinCollateral(0.01); + settings.setMaxCollateral(0.1); storage.write(settings.getClass().getName(), settings); - initMockUser(storage, user); - } - else - { - settings.updateFromStorage(savedSettings); + //initMockUser(storage, user); } } private void initMockUser(Storage storage, User user) { + user.getBankAccounts().clear(); + BankAccount bankAccount1 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.SEPA, "Iban", "Bic"), MockData.getCurrencies().get(0), MockData.getLocales().get(0), - "Main account", + "Main EUR account", "Manfred Karrer", "564613242346", "23432432434" ); - BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.OK_PAY, "Number", "ID"), - MockData.getCurrencies().get(0), - MockData.getLocales().get(0), - "OK account", + user.addBankAccount(bankAccount1); + + BankAccount bankAccount2 = new BankAccount(new BankAccountType(BankAccountType.BankAccountTypeEnum.INTERNATIONAL, "Number", "ID"), + MockData.getCurrencies().get(1), + MockData.getLocales().get(2), + "US account", "Manfred Karrer", "22312123123123123", "asdasdasdas" ); user.addBankAccount(bankAccount2); - user.addBankAccount(bankAccount1); + user.setAccountID(UUID.randomUUID().toString()); + storage.write(user.getClass().getName(), user); } } diff --git a/src/main/java/io/bitsquare/btc/AccountRegistrationWallet.java b/src/main/java/io/bitsquare/btc/AccountRegistrationWallet.java index 71385203fc..e07aa3420c 100644 --- a/src/main/java/io/bitsquare/btc/AccountRegistrationWallet.java +++ b/src/main/java/io/bitsquare/btc/AccountRegistrationWallet.java @@ -42,20 +42,23 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList walletFile = new File(".", "bitsquare_account_reg" + ".wallet"); if (walletFile.exists()) { + FileInputStream walletStream = null; try { - FileInputStream walletStream = new FileInputStream(walletFile); - new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this); + walletStream = new FileInputStream(walletFile); } catch (FileNotFoundException e) { e.printStackTrace(); - } catch (UnreadableWalletException e) + } + + try { - e.printStackTrace(); - } catch (IOException e) + new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this); + } catch (UnreadableWalletException | IOException e) { e.printStackTrace(); } + } else { @@ -69,7 +72,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList saveToFile(walletFile); } catch (IOException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null); } @@ -81,7 +84,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList saveToFile(walletFile); } catch (IOException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); } } diff --git a/src/main/java/io/bitsquare/btc/BtcFormatter.java b/src/main/java/io/bitsquare/btc/BtcFormatter.java index 9a6d2cf9d8..ec5c0cdbd3 100644 --- a/src/main/java/io/bitsquare/btc/BtcFormatter.java +++ b/src/main/java/io/bitsquare/btc/BtcFormatter.java @@ -1,6 +1,12 @@ package io.bitsquare.btc; +import com.google.bitcoin.core.Utils; +import io.bitsquare.gui.util.Converter; +import io.bitsquare.gui.util.Formatter; + import java.math.BigInteger; +import java.text.DecimalFormat; +import java.util.Locale; public class BtcFormatter { @@ -12,4 +18,31 @@ public class BtcFormatter { return satoshis.doubleValue() / BTC.doubleValue(); } + + public static BigInteger stringValueToSatoshis(String value) + { + return Utils.toNanoCoins(String.valueOf(Converter.stringToDouble(value))); + } + + public static BigInteger doubleValueToSatoshis(double value) + { + try + { + // only "." as decimal sep supported by Utils.toNanoCoins + DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH); + String stringValue = decimalFormat.format(value); + return Utils.toNanoCoins(stringValue); + } catch (Exception e) + { + return BigInteger.ZERO; + } + } + + public static String formatSatoshis(BigInteger satoshis, boolean useBTC) + { + if (useBTC) + return Formatter.formatDouble(satoshiToBTC(satoshis), 8) + " BTC"; + else + return Formatter.formatDouble(satoshiToBTC(satoshis), 8); + } } diff --git a/src/main/java/io/bitsquare/btc/Fees.java b/src/main/java/io/bitsquare/btc/Fees.java index 0ea13ec92b..b4e6f5a9ac 100644 --- a/src/main/java/io/bitsquare/btc/Fees.java +++ b/src/main/java/io/bitsquare/btc/Fees.java @@ -1,13 +1,11 @@ package io.bitsquare.btc; -import com.google.bitcoin.core.Transaction; - import java.math.BigInteger; public class Fees { - - public static BigInteger ACCOUNT_REGISTRATION_FEE = Transaction.MIN_NONDUST_OUTPUT;// Utils.toNanoCoins("0.001"); - public static BigInteger OFFER_CREATION_FEE = Transaction.MIN_NONDUST_OUTPUT; // Utils.toNanoCoins("0.001"); - public static BigInteger OFFER_TAKER_FEE = OFFER_CREATION_FEE; + // min dust value lead to exception at for non standard to address pay scripts, so we use a value >= 7860 instead + public static BigInteger ACCOUNT_REGISTRATION_FEE = BigInteger.valueOf(7860);// Utils.toNanoCoins("0.001"); + public static BigInteger OFFER_CREATION_FEE = BigInteger.valueOf(7860); // //Transaction.MIN_NONDUST_OUTPUT; // Utils.toNanoCoins("0.001"); + public static BigInteger OFFER_TAKER_FEE = BigInteger.valueOf(7860); } diff --git a/src/main/java/io/bitsquare/btc/WalletFacade.java b/src/main/java/io/bitsquare/btc/WalletFacade.java index 55006aea50..f0988347b4 100644 --- a/src/main/java/io/bitsquare/btc/WalletFacade.java +++ b/src/main/java/io/bitsquare/btc/WalletFacade.java @@ -1,16 +1,20 @@ package io.bitsquare.btc; import com.google.bitcoin.core.*; +import com.google.bitcoin.crypto.TransactionSignature; import com.google.bitcoin.kits.WalletAppKit; import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.RegTestParams; import com.google.bitcoin.script.Script; import com.google.bitcoin.script.ScriptBuilder; import com.google.bitcoin.utils.Threading; +import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.google.inject.Inject; import io.bitsquare.crypto.CryptoFacade; +import io.bitsquare.gui.util.Popups; import javafx.application.Platform; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,9 +23,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.UUID; - -import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN; +import java.util.concurrent.ExecutionException; /** * That facade delivers wallet functionality from the bitcoinJ library @@ -33,28 +35,44 @@ public class WalletFacade implements WalletEventListener public static final String MAIN_NET = "MAIN_NET"; public static final String TEST_NET = "TEST_NET"; + // for testing trade process between offerer and taker + //public static final String WALLET_PREFIX = "offerer"; // offerer + // public static final String WALLET_PREFIX = "taker"; // offerer + + public static final String WALLET_PREFIX = "bitsquare"; + + private static final Logger log = LoggerFactory.getLogger(WalletFacade.class); - private NetworkParameters networkParameters; + private NetworkParameters params; private WalletAppKit walletAppKit; private CryptoFacade cryptoFacade; - private BlockChainFacade blockChainFacade; // that wallet is used only for the registration process private AccountRegistrationWallet accountRegistrationWallet = null; private List downloadListeners = new ArrayList<>(); private List walletListeners = new ArrayList<>(); + private Wallet wallet; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public WalletFacade(NetworkParameters networkParameters, WalletAppKit walletAppKit, CryptoFacade cryptoFacade, BlockChainFacade blockChainFacade) + public WalletFacade(NetworkParameters params, WalletAppKit walletAppKit, CryptoFacade cryptoFacade) { - this.networkParameters = networkParameters; + this.params = params; this.walletAppKit = walletAppKit; this.cryptoFacade = cryptoFacade; - this.blockChainFacade = blockChainFacade; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public Methods + /////////////////////////////////////////////////////////////////////////////////////////// + public void initWallet() { // Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means @@ -63,11 +81,11 @@ public class WalletFacade implements WalletEventListener // a future version. Threading.USER_THREAD = Platform::runLater; - if (networkParameters == RegTestParams.get()) + if (params == RegTestParams.get()) { walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally. } - else if (networkParameters == MainNetParams.get()) + else if (params == MainNetParams.get()) { // Checkpoints are block headers that ship inside our app: for a new user, we pick the last header // in the checkpoints file and then download the rest from the network. It makes things much faster. @@ -80,16 +98,214 @@ public class WalletFacade implements WalletEventListener // or progress widget to keep the user engaged whilst we initialise, but we don't. walletAppKit.setDownloadListener(new BlockChainDownloadListener()) .setBlockingStartup(false) - .setUserAgent("BitSquare", "1.0"); + .setUserAgent("BitSquare", "0.1"); walletAppKit.startAsync(); walletAppKit.awaitRunning(); + + wallet = walletAppKit.wallet(); + // Don't make the user wait for confirmations for now, as the intention is they're sending it their own money! - getWallet().allowSpendingUnconfirmedTransactions(); - walletAppKit.peerGroup().setMaxConnections(11); + wallet.allowSpendingUnconfirmedTransactions(); + walletAppKit.peerGroup().setMaxConnections(20); - getWallet().addEventListener(this); + wallet.addEventListener(this); - log.info(getWallet().toString()); + log.info(wallet.toString()); + + // testTradeProcess(); + } + + // TODO only temp. for testing trade process between offerer and taker + private void testTradeProcess() + { + //wallet.allowSpendingUnconfirmedTransactions(); + + try + { + String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex; + + BigInteger offererAmount = Utils.toNanoCoins("0.01"); + BigInteger takerAmount = Utils.toNanoCoins("0.02"); + + String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"; + String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7"; + String arbitratorPubKey = ""; + + // 1 offerer creates MS TX and pay in + /* Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey); + tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize()); + */ + + tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000"; + + // 2. taker pay in and sign + /* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex); + tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize()); + tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes()); + tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize()); + */ + + tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000"; + tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"; + tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000"; + + // 3. offerer sign and send + Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex); + + log.info(tx3.toString()); + + } catch (AddressFormatException | InsufficientMoneyException | InterruptedException | ExecutionException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (Exception e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + } + + // 1. offerer + private Transaction offererCreatesMSTxAndAddPayment(BigInteger offererAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException + { + // use that to use the convenient api for getting the best coin selection and fee calculation + // TODO should be constructed manually + Transaction tx = new Transaction(params); + Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey); + tx.addOutput(offererAmount, multiSigOutputScript); + Wallet.SendRequest request = Wallet.SendRequest.forTx(tx); + wallet.completeTx(request); + // TODO remove sig or use SigHash.NONE + //tx.getInput(0).setScriptSig(null); + + /* + IN[0] offerer + OUT[0] MS + OUT[1] offerer change + */ + return tx; + } + + // 2. taker + public Transaction takerAddPaymentAndSign(BigInteger takerAmount, + BigInteger msOutputAmount, + String offererPubKey, + String takerPubKey, + String arbitratorPubKey, + String tx1AsHex + ) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException + { + Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey); + + // use that to use the convenient api for getting the best coin selection and fee calculation + // TODO should be constructed manually + Transaction dummyTx = new Transaction(params); + dummyTx.addOutput(takerAmount, multiSigOutputScript); + wallet.completeTx(Wallet.SendRequest.forTx(dummyTx)); + + Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex)); + tx.addInput(dummyTx.getInput(0)); + tx.addOutput(dummyTx.getOutput(1)); + tx.getOutput(0).setValue(msOutputAmount); + + TransactionInput input = tx.getInput(1); + Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); + ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); + Sha256Hash hash = tx.hashForSignature(1, scriptPubKey, Transaction.SigHash.ALL, false); + ECKey.ECDSASignature ecSig = sigKey.sign(hash); + TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false); + if (scriptPubKey.isSentToRawPubKey()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig)); + else if (scriptPubKey.isSentToAddress()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + else + throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + + input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false); + + /* + IN[0] offerer + IN[1] taker signed + OUT[0] MS + OUT[1] offerer change + OUT[2] taker change + */ + + return tx; + } + + public Transaction offererSignAndSendTx(String tx1AsHex, + String tx2AsHex, + String tx2ConnOutAsHex, + String tx2ScriptSigAsHex) throws Exception + { + Transaction tx = new Transaction(params); + + Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex)); + Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash()); + TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 0, tx1ConnOut); + TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint); + tx1Input.setParent(tx); + tx.addInput(tx1Input); + + Transaction tx2 = new Transaction(params, Utils.parseAsHexOrBase58(tx2AsHex)); + Transaction tx2ConnOut = new Transaction(params, Utils.parseAsHexOrBase58(tx2ConnOutAsHex)); + TransactionOutPoint tx2OutPoint = new TransactionOutPoint(params, 1, tx2ConnOut); + TransactionInput tx2Input = new TransactionInput(params, tx, Utils.parseAsHexOrBase58(tx2ScriptSigAsHex), tx2OutPoint); + tx2Input.setParent(tx); + tx.addInput(tx2Input); + + tx.addOutput(tx2.getOutput(0)); + tx.addOutput(tx2.getOutput(1)); + tx.addOutput(tx2.getOutput(2)); + + TransactionInput input = tx.getInput(0); + Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); + ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); + Sha256Hash hash = tx.hashForSignature(0, scriptPubKey, Transaction.SigHash.ALL, false); + ECKey.ECDSASignature ecSig = sigKey.sign(hash); + TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false); + if (scriptPubKey.isSentToRawPubKey()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig)); + else if (scriptPubKey.isSentToAddress()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + else + throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + + input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false); + + input = tx.getInput(1); + scriptPubKey = input.getConnectedOutput().getScriptPubKey(); + input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false); + + /* + IN[0] offerer signed + IN[1] taker signed + OUT[0] MS + OUT[1] offerer change + OUT[2] taker change + */ + + tx.verify(); + + ListenableFuture broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx); + + FutureCallback callback = new FutureCallback() + { + @Override + public void onSuccess(Transaction transaction) + { + log.info("sendResult onSuccess:" + transaction.toString()); + } + + @Override + public void onFailure(Throwable t) + { + log.warn("sendResult onFailure:" + t.toString()); + Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString()); + } + }; + Futures.addCallback(broadcastComplete, callback); + + return tx; } public void shutDown() @@ -100,6 +316,10 @@ public class WalletFacade implements WalletEventListener walletAppKit.awaitTerminated(); } + /////////////////////////////////////////////////////////////////////////////////////////// + // Listener + /////////////////////////////////////////////////////////////////////////////////////////// + public void addDownloadListener(DownloadListener listener) { downloadListeners.add(listener); @@ -131,23 +351,40 @@ public class WalletFacade implements WalletEventListener } - //MOCK - public KeyPair createNewAddress() - { - return new KeyPair(UUID.randomUUID().toString(), UUID.randomUUID().toString()); - } + /////////////////////////////////////////////////////////////////////////////////////////// + // Trading wallet + /////////////////////////////////////////////////////////////////////////////////////////// public BigInteger getBalance() { - return getWallet().getBalance(Wallet.BalanceType.ESTIMATED); + return wallet.getBalance(Wallet.BalanceType.ESTIMATED); } public String getAddress() { - return getWallet().getKeys().get(0).toAddress(networkParameters).toString(); + return wallet.getKeys().get(0).toAddress(params).toString(); } - // account registration + public String payFee(BigInteger fee, FutureCallback callback) throws InsufficientMoneyException + { + Transaction tx = new Transaction(params); + //TransactionOutput output = new TransactionOutput(params, tx, Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript()); + tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript()); + Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); + + // give fee to miners yet. Later it could be spent to other traders via lottery... + sendRequest.fee = fee; + + Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); + Futures.addCallback(sendResult.broadcastComplete, callback); + + return tx.getHashAsString(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Account registration + /////////////////////////////////////////////////////////////////////////////////////////// + public Address getAccountRegistrationAddress() { return getAccountRegistrationWallet().getAddress(); @@ -155,7 +392,6 @@ public class WalletFacade implements WalletEventListener public String getAccountRegistrationPubKey() { - return Utils.bytesToHexString(getAccountRegistrationWallet().getKey().getPubKey()); } @@ -169,14 +405,9 @@ public class WalletFacade implements WalletEventListener getAccountRegistrationWallet().saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(getAccountRegistrationWallet().getKey(), stringifiedBankAccounts)); } - public boolean verifyAccountRegistration(String address, String hashAsHexStringToVerify, byte[] pubKey, String bankAccountIDs, String signatureBankAccountIDs) - { - return true; - /* - return cryptoFacade.verifySignature(pubKey, bankAccountIDs, signatureBankAccountIDs) - && cryptoFacade.verifyHash(hashAsHexStringToVerify, bankAccountIDs, signatureBankAccountIDs) - && blockChainFacade.verifyEmbeddedData(address); */ - } + /////////////////////////////////////////////////////////////////////////////////////////// + // Getter + /////////////////////////////////////////////////////////////////////////////////////////// public int getRegConfNumBroadcastPeers() { @@ -188,7 +419,16 @@ public class WalletFacade implements WalletEventListener return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet()); } - // WalletEventListener + public ECKey getAccountKey() + { + return getAccountRegistrationWallet().getKey(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Interface implementation: WalletEventListener + /////////////////////////////////////////////////////////////////////////////////////////// + @Override public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) { @@ -202,7 +442,7 @@ public class WalletFacade implements WalletEventListener public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) { for (WalletListener walletListener : walletListeners) - walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(getWallet())); + walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(wallet)); log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString()); } @@ -238,61 +478,33 @@ public class WalletFacade implements WalletEventListener } + /////////////////////////////////////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////////////////////////////////////// + private AccountRegistrationWallet getAccountRegistrationWallet() { if (accountRegistrationWallet == null) - accountRegistrationWallet = new AccountRegistrationWallet(networkParameters, walletAppKit.chain(), walletAppKit.peerGroup()); + accountRegistrationWallet = new AccountRegistrationWallet(params, walletAppKit.chain(), walletAppKit.peerGroup()); return accountRegistrationWallet; } - public String payOfferFee() throws InsufficientMoneyException + private Script getMultiSigScript(String offererPubKey, String takerPubKey, String arbitratorPubKey) { - getWallet(); + ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58(offererPubKey)); + ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58(takerPubKey)); + ECKey arbitratorKey = new ECKey(null, Utils.parseAsHexOrBase58(arbitratorPubKey)); - Script script = new ScriptBuilder() - .op(OP_RETURN) - .build(); - Transaction transaction = new Transaction(networkParameters); - TransactionOutput dataOutput = new TransactionOutput(networkParameters, - transaction, - Transaction.MIN_NONDUST_OUTPUT, - script.getProgram()); - transaction.addOutput(dataOutput); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction); - - // give fee to miners yet. Later it could be spent to other traders via lottery... - sendRequest.fee = Fees.OFFER_CREATION_FEE; - - Wallet.SendResult sendResult = getWallet().sendCoins(sendRequest); - - Futures.addCallback(sendResult.broadcastComplete, new FutureCallback() - { - @Override - public void onSuccess(Transaction result) - { - log.info("sendResult onSuccess:" + result.toString()); - // Platform.runLater(overlayUi::done); - } - - @Override - public void onFailure(Throwable t) - { - log.warn("sendResult onFailure:" + t.toString()); - // We died trying to empty the wallet. - // crashAlert(t); - } - }); - - return transaction.getHashAsString(); + List keys = ImmutableList.of(offererKey, takerKey, arbitratorKey); + return ScriptBuilder.createMultiSigOutputScript(2, keys); } - private Wallet getWallet() - { - return walletAppKit.wallet(); - } - // inner classes + /////////////////////////////////////////////////////////////////////////////////////////// + // Inner classes + /////////////////////////////////////////////////////////////////////////////////////////// + private class BlockChainDownloadListener extends com.google.bitcoin.core.DownloadListener { @Override @@ -326,5 +538,4 @@ public class WalletFacade implements WalletEventListener void onCoinsReceived(BigInteger newBalance); } - } \ No newline at end of file diff --git a/src/main/java/io/bitsquare/btc/WalletUtil.java b/src/main/java/io/bitsquare/btc/WalletUtil.java index 31e86402b8..a7e33715fc 100644 --- a/src/main/java/io/bitsquare/btc/WalletUtil.java +++ b/src/main/java/io/bitsquare/btc/WalletUtil.java @@ -3,10 +3,14 @@ package io.bitsquare.btc; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.Wallet; +import com.google.bitcoin.script.Script; +import com.google.bitcoin.script.ScriptBuilder; import java.math.BigInteger; import java.util.Set; +import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN; + public class WalletUtil { @@ -41,4 +45,11 @@ public class WalletUtil } return null; } + + public static Script getEmptyOP_RETURNScript() + { + return new ScriptBuilder() + .op(OP_RETURN) + .build(); + } } diff --git a/src/main/java/io/bitsquare/crypto/CryptoFacade.java b/src/main/java/io/bitsquare/crypto/CryptoFacade.java index 8d1c756d47..6332252a60 100644 --- a/src/main/java/io/bitsquare/crypto/CryptoFacade.java +++ b/src/main/java/io/bitsquare/crypto/CryptoFacade.java @@ -37,11 +37,9 @@ public class CryptoFacade return Utils.sha256hash160(concatenateChunks(stringifiedBankAccounts, signedBankAccountIDs).getBytes(Charsets.UTF_8)); } - - // TODO MOCK - public String signContract(String contractAsJson) + public String signContract(ECKey key, String contractAsJson) { - return contractAsJson; + return key.signMessage(contractAsJson); } // registration diff --git a/src/main/java/io/bitsquare/di/BitSquareModule.java b/src/main/java/io/bitsquare/di/BitSquareModule.java index 7cba1a3cf1..157ae55f51 100644 --- a/src/main/java/io/bitsquare/di/BitSquareModule.java +++ b/src/main/java/io/bitsquare/di/BitSquareModule.java @@ -62,7 +62,7 @@ class WalletAppKitProvider implements Provider public WalletAppKit get() { - return new WalletAppKit(networkParameters, new File("."), "bitsquare"); + return new WalletAppKit(networkParameters, new File("."), WalletFacade.WALLET_PREFIX); } } diff --git a/src/main/java/io/bitsquare/gui/MainController.java b/src/main/java/io/bitsquare/gui/MainController.java index db5eef0527..675f6ea63b 100644 --- a/src/main/java/io/bitsquare/gui/MainController.java +++ b/src/main/java/io/bitsquare/gui/MainController.java @@ -2,12 +2,12 @@ package io.bitsquare.gui; import com.google.inject.Inject; import io.bitsquare.bank.BankAccount; +import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.WalletFacade; import io.bitsquare.di.GuiceFXMLLoader; import io.bitsquare.gui.components.NetworkSyncPane; import io.bitsquare.gui.market.MarketController; import io.bitsquare.gui.setup.SetupController; -import io.bitsquare.gui.util.Formatter; import io.bitsquare.gui.util.Icons; import io.bitsquare.gui.util.Localisation; import io.bitsquare.trade.Direction; @@ -251,7 +251,7 @@ public class MainController implements Initializable, NavigationController, Wall balanceLabel.setMouseTransparent(true); balanceLabel.setPrefWidth(90); balanceLabel.setId("nav-balance-label"); - balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getBalance(), false)); + balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false)); Label balanceCurrencyLabel = new Label("BTC"); balanceCurrencyLabel.setPadding(new Insets(6, 0, 0, 0)); diff --git a/src/main/java/io/bitsquare/gui/funds/FundsController.java b/src/main/java/io/bitsquare/gui/funds/FundsController.java index b6af7b1f44..850591e6d0 100644 --- a/src/main/java/io/bitsquare/gui/funds/FundsController.java +++ b/src/main/java/io/bitsquare/gui/funds/FundsController.java @@ -3,10 +3,10 @@ package io.bitsquare.gui.funds; import com.google.inject.Inject; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; +import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.WalletFacade; import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; -import io.bitsquare.gui.util.Formatter; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; @@ -63,7 +63,7 @@ public class FundsController implements Initializable, ChildController this.navigationController = navigationController; addressLabel.setText(walletFacade.getAddress()); - balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getBalance(), false)); + balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false)); } diff --git a/src/main/java/io/bitsquare/gui/market/offer/CreateOfferController.java b/src/main/java/io/bitsquare/gui/market/offer/CreateOfferController.java index e1b22c0a56..39ea036a67 100644 --- a/src/main/java/io/bitsquare/gui/market/offer/CreateOfferController.java +++ b/src/main/java/io/bitsquare/gui/market/offer/CreateOfferController.java @@ -1,7 +1,10 @@ package io.bitsquare.gui.market.offer; import com.google.bitcoin.core.InsufficientMoneyException; +import com.google.bitcoin.core.Transaction; +import com.google.common.util.concurrent.FutureCallback; import com.google.inject.Inject; +import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.Fees; import io.bitsquare.btc.WalletFacade; import io.bitsquare.gui.ChildController; @@ -13,6 +16,7 @@ import io.bitsquare.trade.Direction; import io.bitsquare.trade.Offer; import io.bitsquare.trade.Trading; import io.bitsquare.trade.orderbook.OrderBookFilter; +import io.bitsquare.user.Arbitrator; import io.bitsquare.user.User; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -22,10 +26,8 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TabPane; import javafx.scene.control.TextField; -import javafx.scene.image.Image; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; -import org.controlsfx.dialog.Dialogs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,9 +45,11 @@ public class CreateOfferController implements Initializable, ChildController, Wa private Settings settings; private User user; private Direction direction; + private Offer offer; + private int gridRow; private Button placeOfferButton; - private int gridRow; + private TextField collateralTextField, minAmountTextField; @FXML private AnchorPane holderPane; @@ -54,7 +58,12 @@ public class CreateOfferController implements Initializable, ChildController, Wa @FXML public Label buyLabel; @FXML - public TextField volume, amount, price, minAmount; + public TextField volumeTextField, amountTextField, priceTextField; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor + /////////////////////////////////////////////////////////////////////////////////////////// @Inject public CreateOfferController(Trading trading, WalletFacade walletFacade, Settings settings, User user) @@ -65,86 +74,51 @@ public class CreateOfferController implements Initializable, ChildController, Wa this.user = user; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public methods + /////////////////////////////////////////////////////////////////////////////////////////// + + public void setOrderBookFilter(OrderBookFilter orderBookFilter) + { + direction = orderBookFilter.getDirection(); + amountTextField.setText(Formatter.formatPrice(orderBookFilter.getAmount())); + minAmountTextField.setText(Formatter.formatPrice(orderBookFilter.getAmount())); + priceTextField.setText(Formatter.formatPrice(orderBookFilter.getPrice())); + buyLabel.setText(Formatter.formatDirection(direction, false) + ":"); + collateralTextField.setText(Formatter.formatVolume(settings.getMinCollateral())); + updateVolume(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Interface implementation: Initializable + /////////////////////////////////////////////////////////////////////////////////////////// + @Override public void initialize(URL url, ResourceBundle rb) { walletFacade.addRegistrationWalletListener(this); - gridRow = 2; - FormBuilder.addVSpacer(formGridPane, ++gridRow); - FormBuilder.addHeaderLabel(formGridPane, "Offer details:", ++gridRow); - FormBuilder.addTextField(formGridPane, "Bank account type:", Localisation.get(user.getCurrentBankAccount().getBankAccountType().getType().toString()), ++gridRow); - FormBuilder.addTextField(formGridPane, "Bank account currency:", user.getCurrentBankAccount().getCurrency().getCurrencyCode(), ++gridRow); - FormBuilder.addTextField(formGridPane, "Bank account county:", user.getCurrentBankAccount().getCountryLocale().getDisplayCountry(), ++gridRow); - FormBuilder.addTextField(formGridPane, "Accepted countries:", Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()), ++gridRow); - FormBuilder.addTextField(formGridPane, "Accepted languages:", Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()), ++gridRow); - - FormBuilder.addVSpacer(formGridPane, ++gridRow); - Label placeOfferTitle = FormBuilder.addHeaderLabel(formGridPane, "Place offer:", ++gridRow); - - TextField feeLabel = FormBuilder.addTextField(formGridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_CREATION_FEE, true), ++gridRow); - feeLabel.setMouseTransparent(true); - - placeOfferButton = new Button("Place offer"); - formGridPane.add(placeOfferButton, 1, ++gridRow); - placeOfferButton.setDefaultButton(true); - - // handlers - amount.textProperty().addListener(new ChangeListener() - { - @Override - public void changed(ObservableValue observable, String oldValue, String newValue) - { - updateVolume(); - } - }); - - price.textProperty().addListener(new ChangeListener() - { - @Override - public void changed(ObservableValue observable, String oldValue, String newValue) - { - updateVolume(); - } - }); - - placeOfferButton.setOnAction(e -> { - - if (inputValid()) - { - Offer offer = new Offer(user.getAccountID(), - user.getMessageID(), - direction, - Converter.stringToDouble(price.getText()), - Converter.stringToDouble(amount.getText()), - Converter.stringToDouble(minAmount.getText()), - user.getCurrentBankAccount().getBankAccountType().getType(), - user.getCurrentBankAccount().getCurrency(), - user.getCurrentBankAccount().getCountryLocale(), - settings.getRandomArbitrator(), - settings.getAcceptedCountryLocales(), - settings.getAcceptedLanguageLocales()); - - try - { - String txID = trading.placeNewOffer(offer); - formGridPane.getChildren().remove(placeOfferButton); - placeOfferTitle.setText("Transaction sent:"); - buildConfirmationView(txID); - } catch (InsufficientMoneyException e1) - { - Dialogs.create() - .title("Not enough money available") - .message("There is not enough money available. Please pay in first to your wallet.") - .nativeTitleBar() - .lightweight() - .showError(); - } - } - }); + buildScreen(); } + /////////////////////////////////////////////////////////////////////////////////////////// + // Interface implementation: ChildController + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void setNavigationController(NavigationController navigationController) + { + this.navigationController = navigationController; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Interface implementation: WalletFacade.WalletListener + /////////////////////////////////////////////////////////////////////////////////////////// + @Override public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks) { @@ -157,11 +131,119 @@ public class CreateOfferController implements Initializable, ChildController, Wa log.info("onCoinsReceived " + newBalance); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////////////////////////////////////// + + private void buildScreen() + { + gridRow = 1; + minAmountTextField = FormBuilder.addTextField(formGridPane, "Min. Amount:", String.valueOf(settings.getMaxCollateral()), ++gridRow, true, true); + collateralTextField = FormBuilder.addTextField(formGridPane, "Collateral (%):", String.valueOf(settings.getMaxCollateral() * 100), ++gridRow, true, true); + + FormBuilder.addVSpacer(formGridPane, ++gridRow); + FormBuilder.addHeaderLabel(formGridPane, "Offer details:", ++gridRow); + FormBuilder.addTextField(formGridPane, "Bank account type:", Localisation.get(user.getCurrentBankAccount().getBankAccountType().getType().toString()), ++gridRow); + FormBuilder.addTextField(formGridPane, "Bank account currency:", user.getCurrentBankAccount().getCurrency().getCurrencyCode(), ++gridRow); + FormBuilder.addTextField(formGridPane, "Bank account county:", user.getCurrentBankAccount().getCountryLocale().getDisplayCountry(), ++gridRow); + FormBuilder.addTextField(formGridPane, "Accepted countries:", Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()), ++gridRow); + FormBuilder.addTextField(formGridPane, "Accepted languages:", Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()), ++gridRow); + + FormBuilder.addVSpacer(formGridPane, ++gridRow); + Label placeOfferTitle = FormBuilder.addHeaderLabel(formGridPane, "Place offer:", ++gridRow); + + TextField feeLabel = FormBuilder.addTextField(formGridPane, "Offer fee:", BtcFormatter.formatSatoshis(Fees.OFFER_CREATION_FEE, true), ++gridRow); + feeLabel.setMouseTransparent(true); + + placeOfferButton = new Button("Place offer"); + formGridPane.add(placeOfferButton, 1, ++gridRow); + placeOfferButton.setDefaultButton(true); + + // handlers + amountTextField.textProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) + { + updateVolume(); + } + }); + + priceTextField.textProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue 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() + { + @Override + public void onSuccess(Transaction transaction) + { + log.info("sendResult onSuccess:" + transaction.toString()); + offer.setOfferPaymentTxID(transaction.getHashAsString()); + buildConfirmationView(transaction.getHashAsString()); + placeOfferTitle.setText("Transaction sent:"); + formGridPane.getChildren().remove(placeOfferButton); + } + + @Override + public void onFailure(Throwable t) + { + log.warn("sendResult onFailure:" + t.toString()); + Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString()); + } + }; + try + { + trading.placeNewOffer(offer, callback); + + } catch (InsufficientMoneyException e1) + { + Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet."); + } + + }); + } + private void buildConfirmationView(String txID) { FormBuilder.addTextField(formGridPane, "Transaction ID:", txID, ++gridRow, false, true); - ConfirmationComponent confirmationComponent = new ConfirmationComponent(walletFacade, formGridPane, ++gridRow); + new ConfirmationComponent(walletFacade, formGridPane, ++gridRow); Button closeButton = new Button("Close"); formGridPane.add(closeButton, 1, ++gridRow); @@ -176,50 +258,36 @@ public class CreateOfferController implements Initializable, ChildController, Wa }); } - - @Override - public void setNavigationController(NavigationController navigationController) - { - this.navigationController = navigationController; - } - - public void setOrderBookFilter(OrderBookFilter orderBookFilter) - { - direction = orderBookFilter.getDirection(); - amount.setText(Formatter.formatPrice(orderBookFilter.getAmount())); - minAmount.setText(Formatter.formatPrice(orderBookFilter.getAmount())); - price.setText(Formatter.formatPrice(orderBookFilter.getPrice())); - - String iconPath = (direction == Direction.BUY) ? Icons.BUY : Icons.SELL; - buyLabel.setText(Formatter.formatDirection(direction, false) + ":"); - updateVolume(); - } - - //TODO private boolean inputValid() { - return true; + double priceAsDouble = Converter.stringToDouble(priceTextField.getText()); + double minAmountAsDouble = Converter.stringToDouble(minAmountTextField.getText()); + double amountAsDouble = Converter.stringToDouble(amountTextField.getText()); + double collateralAsDouble = Converter.stringToDouble(collateralTextField.getText()); + + return priceAsDouble > 0 && + amountAsDouble > 0 && + minAmountAsDouble > 0 && + minAmountAsDouble <= amountAsDouble && + collateralAsDouble >= settings.getMinCollateral() && + collateralAsDouble <= settings.getMaxCollateral(); } private void updateVolume() { - double amountAsDouble = Converter.stringToDouble(amount.getText()); - double priceAsDouble = Converter.stringToDouble(price.getText()); - volume.setText(Formatter.formatPrice(amountAsDouble * priceAsDouble)); + volumeTextField.setText(Formatter.formatVolume(getVolume())); } - private Image getConfirmIconImage(int numBroadcastPeers, int depthInBlocks) + private double getVolume() { - if (depthInBlocks > 0) - return Icons.getIconImage(Icons.getIconIDForConfirmations(depthInBlocks)); - else - return Icons.getIconImage(Icons.getIconIDForPeersSeenTx(numBroadcastPeers)); + double amountAsDouble = Converter.stringToDouble(amountTextField.getText()); + double priceAsDouble = Converter.stringToDouble(priceTextField.getText()); + return amountAsDouble * priceAsDouble; } - private String getConfirmationsText(int registrationConfirmationNumBroadcastPeers, int registrationConfirmationDepthInBlocks) + private BigInteger getAmountAsBI() { - return registrationConfirmationDepthInBlocks + " confirmation(s) / " + "Seen by " + registrationConfirmationNumBroadcastPeers + " peer(s)"; + return BtcFormatter.stringValueToSatoshis(amountTextField.getText()); } - } diff --git a/src/main/java/io/bitsquare/gui/market/offer/CreateOfferView.fxml b/src/main/java/io/bitsquare/gui/market/offer/CreateOfferView.fxml index fd78330a7b..efd00b20b8 100644 --- a/src/main/java/io/bitsquare/gui/market/offer/CreateOfferView.fxml +++ b/src/main/java/io/bitsquare/gui/market/offer/CreateOfferView.fxml @@ -6,45 +6,34 @@ - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookController.java b/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookController.java index 737db99e78..4e687d5431 100644 --- a/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookController.java +++ b/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookController.java @@ -2,6 +2,7 @@ package io.bitsquare.gui.market.orderbook; import com.google.inject.Inject; import io.bitsquare.bank.BankAccountType; +import io.bitsquare.btc.BtcFormatter; import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.market.offer.CreateOfferController; @@ -30,6 +31,7 @@ import javafx.util.Callback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigInteger; import java.net.URL; import java.text.DecimalFormat; import java.text.ParseException; @@ -155,9 +157,9 @@ public class OrderBookController implements Initializable, ChildController String title = orderBookListItem.getOffer().getDirection() == Direction.BUY ? "Trade: Sell Bitcoin" : "Trade: Buy Bitcoin"; TradeController tradeController = (TradeController) navigationController.navigateToView(NavigationController.TRADE, title); - double requestedAmount = orderBookListItem.getOffer().getAmount(); + BigInteger requestedAmount = orderBookListItem.getOffer().getAmount(); if (!amount.getText().equals("")) - requestedAmount = Converter.stringToDouble(amount.getText()); + requestedAmount = BtcFormatter.stringValueToSatoshis(amount.getText()); tradeController.initWithData(orderBookListItem.getOffer(), requestedAmount); } diff --git a/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookListItem.java b/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookListItem.java index 18be3e298c..25e9504e9c 100644 --- a/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookListItem.java +++ b/src/main/java/io/bitsquare/gui/market/orderbook/OrderBookListItem.java @@ -22,7 +22,7 @@ public class OrderBookListItem this.offer = offer; this.price.set(Formatter.formatPrice(offer.getPrice())); - this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount(), offer.getMinAmount())); + this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount().doubleValue(), offer.getMinAmount().doubleValue())); this.volume.set(Formatter.formatVolumeWithMinVolume(offer.getVolume(), offer.getMinVolume())); } diff --git a/src/main/java/io/bitsquare/gui/market/trade/TradeController.java b/src/main/java/io/bitsquare/gui/market/trade/TradeController.java index cc5d929d2d..630fcdbb91 100644 --- a/src/main/java/io/bitsquare/gui/market/trade/TradeController.java +++ b/src/main/java/io/bitsquare/gui/market/trade/TradeController.java @@ -1,5 +1,8 @@ package io.bitsquare.gui.market.trade; +import com.google.bitcoin.core.InsufficientMoneyException; +import com.google.bitcoin.core.Transaction; +import com.google.common.util.concurrent.FutureCallback; import com.google.inject.Inject; import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BtcFormatter; @@ -23,7 +26,6 @@ import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; -import org.controlsfx.dialog.Dialogs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ import java.util.ResourceBundle; public class TradeController implements Initializable, ChildController, WalletFacade.WalletListener { private static final Logger log = LoggerFactory.getLogger(TradeController.class); - private static final int SIM_DELAY = 1000; + private static final int SIM_DELAY = 2000; private Trading trading; private WalletFacade walletFacade; @@ -44,14 +46,14 @@ public class TradeController implements Initializable, ChildController, WalletFa private Offer offer; private Trade trade; private Contract contract; - private double requestedAmount; + private BigInteger requestedAmount; private boolean offererIsOnline; private int row; private List processStepItems = new ArrayList(); private NavigationController navigationController; - private TextField amountTextField, totalToPayLabel, totalLabel; + private TextField amountTextField, totalToPayLabel, totalLabel, collateralTextField; private Label statusTextField, infoLabel; private Button nextButton; private ProgressBar progressBar; @@ -81,14 +83,14 @@ public class TradeController implements Initializable, ChildController, WalletFa // Public methods /////////////////////////////////////////////////////////////////////////////////////////// - public void initWithData(Offer offer, double requestedAmount) + public void initWithData(Offer offer, BigInteger requestedAmount) { this.offer = offer; - this.requestedAmount = requestedAmount > 0 ? requestedAmount : offer.getAmount(); + this.requestedAmount = requestedAmount.compareTo(BigInteger.ZERO) > 0 ? requestedAmount : offer.getAmount(); - trade = trading.createNewTrade(offer); + trade = trading.createTrade(offer); trade.setTradeAmount(requestedAmount); - contract = trading.createNewContract(trade); + contract = trading.createContract(trade); processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC")); processStepItems.add(new ProcessStepItem("Bank transfer")); @@ -148,19 +150,22 @@ public class TradeController implements Initializable, ChildController, WalletFa row = -1; FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row); - amountTextField = FormBuilder.addTextField(gridPane, "Amount BTC:", Formatter.formatAmount(requestedAmount), ++row, true, true); + amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.formatSatoshis(requestedAmount, false), ++row, true, true); amountTextField.textProperty().addListener(e -> { applyVolume(); + applyCollateral(); totalToPayLabel.setText(getTotalToPay()); }); - Label amountRangeLabel = new Label("(" + Formatter.formatAmount(offer.getMinAmount()) + " - " + Formatter.formatAmount(offer.getAmount()) + ")"); + Label amountRangeLabel = new Label("(" + BtcFormatter.formatSatoshis(offer.getMinAmount(), false) + " - " + BtcFormatter.formatSatoshis(offer.getAmount(), false) + ")"); gridPane.add(amountRangeLabel, 2, row); - FormBuilder.addTextField(gridPane, "Price:", Formatter.formatPriceWithCurrencyPair(offer.getPrice(), offer.getCurrency()), ++row); - totalLabel = FormBuilder.addTextField(gridPane, "Total:", Formatter.formatVolume(getVolume(), offer.getCurrency()), ++row); - FormBuilder.addTextField(gridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_TAKER_FEE, true), ++row); - totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay:", getTotalToPay(), ++row); + FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", Formatter.formatPrice(offer.getPrice()), ++row); + totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row); + collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row); + applyCollateral(); + FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE, false), ++row); + totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPay(), ++row); nextButton = FormBuilder.addButton(gridPane, "Take offer and pay", ++row); nextButton.setDefaultButton(true); @@ -247,9 +252,9 @@ public class TradeController implements Initializable, ChildController, WalletFa progressIndicatorHolder.getChildren().addAll(progressIndicator); gridPane.add(progressIndicatorHolder, 1, row); - trade.setTradeAmount(Converter.stringToDouble(amountTextField.getText())); - trading.sendTakeOfferRequest(trade); + trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText())); + trading.sendTakeOfferRequest(trade); Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> { onTakeOfferRequestConfirmed(); progressBar.setProgress(1.0 / 3.0); @@ -259,14 +264,37 @@ public class TradeController implements Initializable, ChildController, WalletFa private void onTakeOfferRequestConfirmed() { - trading.payOfferFee(trade); + FutureCallback callback = new FutureCallback() + { + @Override + public void onSuccess(Transaction transaction) + { + log.info("sendResult onSuccess:" + transaction.toString()); + trade.setTakeOfferFeeTxID(transaction.getHashAsString()); - statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation."); - Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> { - onOfferFeePaymentConfirmed(); - progressBar.setProgress(2.0 / 3.0); - return null; - }); + statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation."); + Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> { + onOfferFeePaymentConfirmed(); + progressBar.setProgress(2.0 / 3.0); + return null; + }); + } + + @Override + public void onFailure(Throwable t) + { + log.warn("sendResult onFailure:" + t.toString()); + Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString()); + } + }; + + try + { + trading.payOfferFee(trade, callback); + } catch (InsufficientMoneyException e) + { + Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet."); + } } private void onOfferFeePaymentConfirmed() @@ -282,14 +310,10 @@ public class TradeController implements Initializable, ChildController, WalletFa private void onUserDetailsReceived() { - if (!walletFacade.verifyAccountRegistration(offer.getAccountID(), null, null, null, null)) + if (!blockChainFacade.verifyEmbeddedData(offer.getAccountID())) { - Dialogs.create() - .title("Offerers bank account is blacklisted") - .message("Offerers bank account is blacklisted.") - .nativeTitleBar() - .lightweight() - .showError(); + Popups.openErrorPopup("Offerers bank account is blacklisted", "Offerers bank account is blacklisted."); + return; } trading.signContract(contract); @@ -337,8 +361,19 @@ public class TradeController implements Initializable, ChildController, WalletFa gridPane.getChildren().clear(); row = -1; FormBuilder.addHeaderLabel(gridPane, "Trade successfully completed", ++row); - FormBuilder.addTextField(gridPane, "You have payed:", getTotalToPay(), ++row); - FormBuilder.addTextField(gridPane, "You have received:", getTotalToReceive(), ++row); + FormBuilder.addTextField(gridPane, "You have payed in total (BTC):", getTotalToPay(), ++row); + if (takerIsSelling()) + { + FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false), ++row); + FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row); + } + else + { + FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false), ++row); + FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", Formatter.formatVolume(getVolume()), ++row); + FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.formatSatoshis(offer.getAmount(), false), ++row); + } + gridPane.add(nextButton, 1, ++row); } @@ -355,8 +390,8 @@ public class TradeController implements Initializable, ChildController, WalletFa // Other Private methods private boolean tradeAmountValid() { - double tradeAmount = Converter.stringToDouble(amountTextField.getText()); - return tradeAmount <= offer.getAmount() && tradeAmount >= offer.getMinAmount(); + BigInteger tradeAmount = BtcFormatter.stringValueToSatoshis(amountTextField.getText()); + return tradeAmount.compareTo(offer.getAmount()) <= 0 && tradeAmount.compareTo(offer.getMinAmount()) >= 0; } private boolean takerIsSelling() @@ -388,28 +423,31 @@ public class TradeController implements Initializable, ChildController, WalletFa private String getTotalToPay() { - String result = ""; if (takerIsSelling()) { - double btcValue = Converter.stringToDouble(amountTextField.getText()) + BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE)/* + - offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/; - result = Formatter.formatAmount(btcValue, true, true); + return BtcFormatter.formatSatoshis(getAmountInSatoshis().add(Fees.OFFER_TAKER_FEE).add(getCollateralInSatoshis()), false); } else { - double btcValue = BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE) /*+ offer.getConstraints().getCollateral() * Converter.stringToDouble(amountTextField.getText())*/; - result = Formatter.formatAmount(btcValue, true, true) + "\n" + Formatter.formatVolume(getVolume(), offer.getCurrency()); + return BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE.add(getCollateralInSatoshis()), false) + "\n" + + Formatter.formatVolume(getVolume(), offer.getCurrency()); } - return result; } - private String getTotalToReceive() + + private void applyCollateral() { - if (takerIsSelling()) - return Formatter.formatVolume(getVolume(), offer.getCurrency()); - else - return Formatter.formatAmount(offer.getAmount(), true, true); + collateralTextField.setText(BtcFormatter.formatSatoshis(getCollateralInSatoshis(), false)); } + private BigInteger getCollateralInSatoshis() + { + return BtcFormatter.doubleValueToSatoshis(Converter.stringToDouble(amountTextField.getText()) * offer.getCollateral()); + } + + private BigInteger getAmountInSatoshis() + { + return BtcFormatter.stringValueToSatoshis(amountTextField.getText()); + } } diff --git a/src/main/java/io/bitsquare/gui/msg/MockDelay.java b/src/main/java/io/bitsquare/gui/msg/MockDelay.java index 5ca9ffee47..61af376bec 100644 --- a/src/main/java/io/bitsquare/gui/msg/MockDelay.java +++ b/src/main/java/io/bitsquare/gui/msg/MockDelay.java @@ -9,24 +9,14 @@ public class MockDelay { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Task(expectedMsg)); + // max timeout 5 sec try { - try - { - // max timeout 5 sec - return future.get(5, TimeUnit.SECONDS); - } catch (InterruptedException e) - { - e.printStackTrace(); - } catch (ExecutionException e) - { - e.printStackTrace(); - } - } catch (TimeoutException e) + return future.get(5, TimeUnit.SECONDS); + } catch (InterruptedException | TimeoutException | ExecutionException e) { - System.out.println("Terminated!"); + e.printStackTrace(); } - executor.shutdownNow(); return null; } diff --git a/src/main/java/io/bitsquare/gui/setup/SetupController.java b/src/main/java/io/bitsquare/gui/setup/SetupController.java index cb801bec33..82d97c1b3a 100644 --- a/src/main/java/io/bitsquare/gui/setup/SetupController.java +++ b/src/main/java/io/bitsquare/gui/setup/SetupController.java @@ -6,6 +6,7 @@ import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import io.bitsquare.bank.BankAccount; import io.bitsquare.bank.BankAccountType; +import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.WalletFacade; import io.bitsquare.gui.ChildController; import io.bitsquare.gui.NavigationController; @@ -14,8 +15,8 @@ import io.bitsquare.gui.components.NetworkSyncPane; import io.bitsquare.gui.components.processbar.ProcessStepBar; import io.bitsquare.gui.components.processbar.ProcessStepItem; import io.bitsquare.gui.util.FormBuilder; -import io.bitsquare.gui.util.Formatter; import io.bitsquare.gui.util.Localisation; +import io.bitsquare.gui.util.Popups; import io.bitsquare.gui.util.Verification; import io.bitsquare.storage.Storage; import io.bitsquare.user.User; @@ -29,7 +30,6 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.util.StringConverter; -import org.controlsfx.dialog.Dialogs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,13 +41,15 @@ public class SetupController implements Initializable, ChildController, WalletFa { private static final Logger log = LoggerFactory.getLogger(SetupController.class); - private User user; + private final User user; private final WalletFacade walletFacade; - private Storage storage; - private List processStepItems = new ArrayList(); + private final Storage storage; + private final List processStepItems = new ArrayList<>(); private NavigationController navigationController; private TextField balanceLabel, accountTitle, accountHolderName, accountPrimaryID, accountSecondaryID; - private ComboBox countryComboBox, bankTransferTypeComboBox, currencyComboBox; + private ComboBox countryComboBox; + private ComboBox bankTransferTypeComboBox; + private ComboBox currencyComboBox; private Button addBankAccountButton; @FXML @@ -133,7 +135,7 @@ public class SetupController implements Initializable, ChildController, WalletFa public void onCoinsReceived(BigInteger newBalance) { updateCreateAccountButton(); - balanceLabel.setText(Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true)); + balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true)); log.info("onCoinsReceived " + newBalance); } @@ -161,7 +163,7 @@ public class SetupController implements Initializable, ChildController, WalletFa AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard")); - balanceLabel = FormBuilder.addTextField(gridPane, "Balance:", Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row); + balanceLabel = FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row); new ConfirmationComponent(walletFacade, gridPane, ++row); @@ -194,7 +196,7 @@ public class SetupController implements Initializable, ChildController, WalletFa gridPane.getChildren().clear(); int row = -1; - bankTransferTypeComboBox = FormBuilder.addComboBox(gridPane, "Bank account type:", Utils.getAllBankAccountTypes(), ++row); + bankTransferTypeComboBox = FormBuilder.addBankAccountComboBox(gridPane, "Bank account type:", Utils.getAllBankAccountTypes(), ++row); bankTransferTypeComboBox.setConverter(new StringConverter() { @Override @@ -216,7 +218,7 @@ public class SetupController implements Initializable, ChildController, WalletFa accountPrimaryID = FormBuilder.addInputField(gridPane, "Bank account primary ID", "", ++row); accountSecondaryID = FormBuilder.addInputField(gridPane, "Bank account secondary ID:", "", ++row); - currencyComboBox = FormBuilder.addComboBox(gridPane, "Currency used for bank account:", Utils.getAllCurrencies(), ++row); + currencyComboBox = FormBuilder.addCurrencyComboBox(gridPane, "Currency used for bank account:", Utils.getAllCurrencies(), ++row); currencyComboBox.setPromptText("Select currency"); currencyComboBox.setConverter(new StringConverter() { @@ -233,7 +235,7 @@ public class SetupController implements Initializable, ChildController, WalletFa } }); - countryComboBox = FormBuilder.addComboBox(gridPane, "Country of bank account:", Utils.getAllLocales(), ++row); + countryComboBox = FormBuilder.addLocalesComboBox(gridPane, "Country of bank account:", Utils.getAllLocales(), ++row); countryComboBox.setPromptText("Select country"); countryComboBox.setConverter(new StringConverter() { @@ -267,11 +269,10 @@ public class SetupController implements Initializable, ChildController, WalletFa bankTransferTypeComboBox.valueProperty().addListener((ov, oldValue, newValue) -> { if (newValue != null && newValue instanceof BankAccountType) { - BankAccountType bankAccountType = (BankAccountType) newValue; accountPrimaryID.setText(""); - accountPrimaryID.setPromptText(bankAccountType.getPrimaryIDName()); + accountPrimaryID.setPromptText(newValue.getPrimaryIDName()); accountSecondaryID.setText(""); - accountSecondaryID.setPromptText(bankAccountType.getSecondaryIDName()); + accountSecondaryID.setPromptText(newValue.getSecondaryIDName()); checkCreateAccountButtonState(); } @@ -303,19 +304,14 @@ public class SetupController implements Initializable, ChildController, WalletFa { walletFacade.sendRegistrationTx(user.getStringifiedBankAccounts()); user.setAccountID(walletFacade.getAccountRegistrationAddress().toString()); - user.setMessageID(walletFacade.getAccountRegistrationPubKey().toString()); + user.setMessageID(walletFacade.getAccountRegistrationPubKey()); storage.write(user.getClass().getName(), user); processStepBar.next(); buildStep2(); } catch (InsufficientMoneyException e1) { - Dialogs.create() - .title("Not enough money available") - .message("There is not enough money available. Please pay in first to your wallet.") - .nativeTitleBar() - .lightweight() - .showError(); + Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet."); } } }); @@ -334,7 +330,7 @@ public class SetupController implements Initializable, ChildController, WalletFa FormBuilder.addHeaderLabel(gridPane, "Registration complete", ++row); FormBuilder.addTextField(gridPane, "Registration address:", walletFacade.getAccountRegistrationAddress().toString(), ++row); - FormBuilder.addTextField(gridPane, "Balance:", Formatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row); + FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row); Button closeButton = FormBuilder.addButton(gridPane, "Close", ++row); closeButton.setDefaultButton(true); @@ -409,9 +405,9 @@ public class SetupController implements Initializable, ChildController, WalletFa if (verifyBankAccountData()) { BankAccount bankAccount = new BankAccount( - (BankAccountType) bankTransferTypeComboBox.getSelectionModel().getSelectedItem(), - (Currency) currencyComboBox.getSelectionModel().getSelectedItem(), - (Locale) countryComboBox.getSelectionModel().getSelectedItem(), + bankTransferTypeComboBox.getSelectionModel().getSelectedItem(), + currencyComboBox.getSelectionModel().getSelectedItem(), + countryComboBox.getSelectionModel().getSelectedItem(), accountTitle.getText(), accountHolderName.getText(), accountPrimaryID.getText(), diff --git a/src/main/java/io/bitsquare/gui/setup/SetupView.fxml b/src/main/java/io/bitsquare/gui/setup/SetupView.fxml index 9a59919c97..4da8104c59 100644 --- a/src/main/java/io/bitsquare/gui/setup/SetupView.fxml +++ b/src/main/java/io/bitsquare/gui/setup/SetupView.fxml @@ -7,25 +7,23 @@ xmlns:fx="http://javafx.com/fxml"> - - -