integration of TomP2P for payment process (part 1), fund screen, setup redesign, orders screen
|
@ -7,9 +7,7 @@ This is just a first very basic GUI prototype with mock data.
|
|||
There is only the trade process for Sell BTC and the role of the offer taker modelled yet.
|
||||
|
||||
The project use Java 8 and Maven.
|
||||
|
||||
We use bitcoinj library as a submodule. To get the project with the submodule included use:
|
||||
git clone --recursive git://github.com/bitsquare/bitsquare
|
||||
We use the bitcoinj library and TomP2P for DHT and messaging.
|
||||
|
||||
### Implemented (prototype level):
|
||||
* Screen for orderbook with filtering mock offers by amount, price and order type (buy, sell)
|
||||
|
@ -22,12 +20,12 @@ git clone --recursive git://github.com/bitsquare/bitsquare
|
|||
* Pay in to MS fund
|
||||
* Payout from MS fund
|
||||
* TomP2P as messaging lib integrated and basic use cases in msg screen implemented: orderbook, add order, remove order, find peer, chat with peer
|
||||
* Payment process until wait for bank transfer implemented with messaging
|
||||
|
||||
### Next steps:
|
||||
* Implement messaging with TomP2P for registration, orderbook and payment process
|
||||
* Payment process after wait for bank transfer implemented with messaging
|
||||
* Arbitrator integration
|
||||
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
|
||||
* Verify registration and fee payments tx and get them from the blockchain
|
||||
* ...
|
||||
|
||||
|
||||
|
|
6
TODO.txt
|
@ -2,11 +2,11 @@
|
|||
- settings
|
||||
|
||||
low prio:
|
||||
- tx confirm. not working correct and reliable
|
||||
- add settings after setup
|
||||
- start with orderbook and open registration when user interacts with orderbook (take offer, create offer)
|
||||
- settings screen
|
||||
- return to setup when unregistered, change/add bank accounts from settings
|
||||
- BigInteger for all btc values
|
||||
- formatting
|
||||
- validation
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit eda6dccf6dc015df613a19f1fc0b5c3c98546185
|
43
pom.xml
|
@ -11,6 +11,7 @@
|
|||
<description>A P2P Fiat-Bitcoin Exchange</description>
|
||||
<url>https://www.bitsquare.io</url>
|
||||
|
||||
<!--
|
||||
<parent>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj-parent</artifactId>
|
||||
|
@ -18,6 +19,8 @@
|
|||
<relativePath>libs/bitcoinj/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
-->
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU AFFERO GENERAL PUBLIC LICENSE</name>
|
||||
|
@ -97,13 +100,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google</groupId>
|
||||
<artifactId>bitcoinj</artifactId>
|
||||
<version>0.12-SNAPSHOT</version>
|
||||
<version>0.11.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.tomp2p</groupId>
|
||||
<artifactId>TomP2P</artifactId>
|
||||
<version>4.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -112,20 +120,13 @@
|
|||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
|
@ -146,12 +147,6 @@
|
|||
<version>16.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aquafx-project</groupId>
|
||||
<artifactId>aquafx</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
|
@ -177,9 +172,9 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.tomp2p</groupId>
|
||||
<artifactId>TomP2P</artifactId>
|
||||
<version>4.4</version>
|
||||
<groupId>com.madgag.spongycastle</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>1.50.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
@ -187,11 +182,11 @@
|
|||
<reporting>
|
||||
<plugins>
|
||||
<!-- Generate cross-referenced HTML source code listing -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.1</version>
|
||||
</plugin>
|
||||
<!-- <plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.1</version>
|
||||
</plugin> -->
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.bitsquare;
|
||||
|
||||
import com.google.bitcoin.core.ECKey;
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
@ -14,6 +15,7 @@ import io.bitsquare.settings.Settings;
|
|||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.MockData;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Parent;
|
||||
|
@ -22,21 +24,23 @@ import javafx.stage.Stage;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BitSquare extends Application
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BitSquare.class);
|
||||
public static String ID = "";
|
||||
private WalletFacade walletFacade;
|
||||
private MessageFacade messageFacade;
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
log.debug("Startup: main");
|
||||
if (args.length > 0)
|
||||
WalletFacade.WALLET_PREFIX = args[0];
|
||||
else
|
||||
WalletFacade.WALLET_PREFIX = "bitsquare";
|
||||
ID = args[0];
|
||||
|
||||
launch(args);
|
||||
}
|
||||
|
@ -44,9 +48,12 @@ public class BitSquare extends Application
|
|||
@Override
|
||||
public void start(Stage stage) throws Exception
|
||||
{
|
||||
log.debug("Startup: start");
|
||||
final Injector injector = Guice.createInjector(new BitSquareModule());
|
||||
walletFacade = injector.getInstance(WalletFacade.class);
|
||||
|
||||
messageFacade = injector.getInstance(MessageFacade.class);
|
||||
log.debug("Startup: messageFacade, walletFacade inited");
|
||||
|
||||
// apply stored data
|
||||
final User user = injector.getInstance(User.class);
|
||||
|
@ -59,7 +66,11 @@ public class BitSquare extends Application
|
|||
|
||||
settings.updateFromStorage((Settings) storage.read(settings.getClass().getName()));
|
||||
|
||||
stage.setTitle("BitSquare (" + WalletFacade.WALLET_PREFIX + ")");
|
||||
if (ID.length() > 0)
|
||||
stage.setTitle("BitSquare (" + ID + ")");
|
||||
else
|
||||
stage.setTitle("BitSquare");
|
||||
|
||||
|
||||
GuiceFXMLLoader.setInjector(injector);
|
||||
final GuiceFXMLLoader loader = new GuiceFXMLLoader(getClass().getResource("/io/bitsquare/gui/MainView.fxml"), Localisation.getResourceBundle());
|
||||
|
@ -71,12 +82,13 @@ public class BitSquare extends Application
|
|||
final String global = getClass().getResource("/io/bitsquare/gui/global.css").toExternalForm();
|
||||
scene.getStylesheets().setAll(global);
|
||||
|
||||
stage.setMinWidth(740);
|
||||
stage.setMinWidth(800);
|
||||
stage.setMinHeight(400);
|
||||
stage.setWidth(800);
|
||||
stage.setHeight(600);
|
||||
|
||||
stage.show();
|
||||
log.debug("Startup: stage displayed");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,20 +116,22 @@ public class BitSquare extends Application
|
|||
//settings.addAcceptedCountryLocale(Locale.getDefault());
|
||||
settings.addAcceptedCountryLocale(MockData.getLocales().get(0));
|
||||
settings.addAcceptedCountryLocale(new Locale("en", "US"));
|
||||
settings.addAcceptedCountryLocale(new Locale("de", "DE"));
|
||||
settings.addAcceptedCountryLocale(new Locale("es", "ES"));
|
||||
|
||||
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);
|
||||
settings.getAcceptedArbitrators().clear();
|
||||
settings.addAcceptedArbitrator(new Arbitrator("uid_1", "Charlie Boom", Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Charly_Boom", 1, 10, Utils.toNanoCoins("0.01")));
|
||||
settings.addAcceptedArbitrator(new Arbitrator("uid_2", "Tom Shang", Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Tom_Shang", 0, 1, Utils.toNanoCoins("0.001")));
|
||||
settings.addAcceptedArbitrator(new Arbitrator("uid_3", "Edward Snow", Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Edward_Swow", 2, 5, Utils.toNanoCoins("0.05")));
|
||||
settings.addAcceptedArbitrator(new Arbitrator("uid_4", "Julian Sander", Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Julian_Sander", 0, 20, Utils.toNanoCoins("0.1")));
|
||||
|
||||
settings.setMinCollateral(1);
|
||||
settings.setMaxCollateral(10);
|
||||
|
||||
storage.write(settings.getClass().getName(), settings);
|
||||
|
||||
|
@ -125,6 +139,22 @@ public class BitSquare extends Application
|
|||
}
|
||||
}
|
||||
|
||||
private String getMessagePubKey()
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
keyGen.initialize(1024);
|
||||
KeyPair generatedKeyPair = keyGen.genKeyPair();
|
||||
PublicKey pubKey = generatedKeyPair.getPublic();
|
||||
return DSAKeyUtil.getHexStringFromPublicKey(pubKey);
|
||||
} catch (Exception e2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void initMockUser(Storage storage, User user)
|
||||
{
|
||||
user.getBankAccounts().clear();
|
||||
|
@ -149,7 +179,7 @@ public class BitSquare extends Application
|
|||
);
|
||||
user.addBankAccount(bankAccount2);
|
||||
|
||||
user.setAccountID(UUID.randomUUID().toString());
|
||||
user.setAccountID(Utils.bytesToHexString(new ECKey().getPubKey()));
|
||||
|
||||
storage.write(user.getClass().getName(), user);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,30 @@
|
|||
package io.bitsquare.msg;
|
||||
package io.bitsquare;
|
||||
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerMaker;
|
||||
import net.tomp2p.peers.Number160;
|
||||
|
||||
public class BootstrapMasterPeer
|
||||
public class RelayNode
|
||||
{
|
||||
private static Peer masterPeer = null;
|
||||
public static Number160 ID = Number160.createHash(1);
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
INSTANCE(5000);
|
||||
if (args.length == 1)
|
||||
INSTANCE(new Integer(args[0]));
|
||||
else
|
||||
INSTANCE(5000);
|
||||
}
|
||||
|
||||
public static Peer INSTANCE(int port) throws Exception
|
||||
{
|
||||
if (masterPeer == null)
|
||||
{
|
||||
masterPeer = new PeerMaker(ID).setPorts(port).makeAndListen();
|
||||
|
||||
// masterPeer = new PeerMaker(ID).setPorts(port).setBagSize(100).makeAndListen(); // setBagSize cause sync problems...
|
||||
masterPeer.getBroadcastRPC().getConnectionBean().getConnectionReservation().reserve(10).awaitUninterruptibly();
|
||||
}
|
||||
return masterPeer;
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ public class BankAccount implements Serializable
|
|||
{
|
||||
|
||||
private static final long serialVersionUID = 1792577576443221268L;
|
||||
private static final long VERSION = 1;
|
||||
|
||||
private BankAccountType bankAccountType;
|
||||
private String accountPrimaryID;
|
||||
|
@ -80,17 +79,18 @@ public class BankAccount implements Serializable
|
|||
return accountTitle;
|
||||
}
|
||||
|
||||
// Changes of that structure must be reflected in VERSION updates
|
||||
public String getStringifiedBankAccount()
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "{" +
|
||||
"type=" + bankAccountType +
|
||||
", primaryID='" + accountPrimaryID + '\'' +
|
||||
", secondaryID='" + accountSecondaryID + '\'' +
|
||||
", holderName='" + accountHolderName + '\'' +
|
||||
", currency='" + currency.getCurrencyCode() + '\'' +
|
||||
", country='" + countryLocale.getCountry() + '\'' +
|
||||
", v='" + VERSION + '\'' +
|
||||
return "BankAccount{" +
|
||||
"bankAccountType=" + bankAccountType +
|
||||
", accountPrimaryID='" + accountPrimaryID + '\'' +
|
||||
", accountSecondaryID='" + accountSecondaryID + '\'' +
|
||||
", accountHolderName='" + accountHolderName + '\'' +
|
||||
", countryLocale=" + countryLocale +
|
||||
", currency=" + currency +
|
||||
", uid='" + uid + '\'' +
|
||||
", accountTitle='" + accountTitle + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ public class BankAccountType implements Serializable
|
|||
|
||||
private static final long serialVersionUID = -8772708150197835288L;
|
||||
|
||||
public static enum BankAccountTypeEnum
|
||||
{
|
||||
SEPA, WIRE, INTERNATIONAL, OK_PAY, NET_TELLER, PERFECT_MONEY, OTHER
|
||||
}
|
||||
|
||||
private BankAccountTypeEnum type;
|
||||
private String primaryIDName;
|
||||
private String secondaryIDName;
|
||||
|
@ -40,8 +45,4 @@ public class BankAccountType implements Serializable
|
|||
return type.toString();
|
||||
}
|
||||
|
||||
public static enum BankAccountTypeEnum
|
||||
{
|
||||
SEPA, WIRE, INTERNATIONAL, OK_PAY, NET_TELLER, PERFECT_MONEY, OTHER
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import com.google.bitcoin.store.UnreadableWalletException;
|
|||
import com.google.bitcoin.store.WalletProtobufSerializer;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import io.bitsquare.BitSquare;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -39,7 +41,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
|
|||
this.chain = chain;
|
||||
this.peerGroup = peerGroup;
|
||||
|
||||
walletFile = new File(".", "bitsquare_account_reg" + ".wallet");
|
||||
walletFile = new File(Utilities.getRootDir() + "account_reg_" + BitSquare.ID + ".wallet");
|
||||
if (walletFile.exists())
|
||||
{
|
||||
FileInputStream walletStream = null;
|
||||
|
@ -74,7 +76,10 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
|
|||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null);
|
||||
|
||||
allowSpendingUnconfirmedTransactions();
|
||||
}
|
||||
|
||||
void shutDown()
|
||||
|
@ -166,7 +171,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
|
|||
for (WalletFacade.WalletListener walletListener : walletListeners)
|
||||
walletListener.onCoinsReceived(newBalance);
|
||||
|
||||
log.info("onCoinsReceived");
|
||||
// log.info("onCoinsReceived");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,7 +180,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
|
|||
for (WalletFacade.WalletListener walletListener : walletListeners)
|
||||
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(this));
|
||||
|
||||
log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
|
||||
// log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,7 +198,7 @@ public class AccountRegistrationWallet extends Wallet implements WalletEventList
|
|||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
log.info("onWalletChanged");
|
||||
// log.info("onWalletChanged");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,11 +16,19 @@ public class BlockChainFacade
|
|||
|
||||
}
|
||||
|
||||
public boolean verifyEmbeddedData(String address)
|
||||
//TODO
|
||||
public boolean isAccountBlackListed(String accountID, BankAccount bankAccount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO
|
||||
public boolean verifyAccountRegistration()
|
||||
{
|
||||
return true;
|
||||
|
||||
// tx id 76982adc582657b2eb68f3e43341596a68aadc4ef6b9590e88e93387d4d5d1f9
|
||||
// address: mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj
|
||||
return true;
|
||||
/*
|
||||
if (findAddressInBlockChain(address) && isFeePayed(address))
|
||||
return getDataForTxWithAddress(address) != null;
|
||||
|
@ -49,14 +57,14 @@ public class BlockChainFacade
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean isAccountIDBlacklisted(String accountID)
|
||||
private boolean isAccountIDBlacklisted(String accountID)
|
||||
{
|
||||
// TODO
|
||||
// check if accountID is on blacklist
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isBankAccountBlacklisted(BankAccount bankAccount)
|
||||
private boolean isBankAccountBlacklisted(BankAccount bankAccount)
|
||||
{
|
||||
// TODO
|
||||
// check if accountID is on blacklist
|
||||
|
|
|
@ -3,6 +3,8 @@ package io.bitsquare.btc;
|
|||
import com.google.bitcoin.core.Utils;
|
||||
import io.bitsquare.gui.util.Converter;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
|
@ -10,6 +12,8 @@ import java.util.Locale;
|
|||
|
||||
public class BtcFormatter
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BtcFormatter.class);
|
||||
|
||||
public static BigInteger BTC = new BigInteger("100000000");
|
||||
public static BigInteger mBTC = new BigInteger("100000");
|
||||
|
||||
|
@ -30,10 +34,13 @@ public class BtcFormatter
|
|||
{
|
||||
// only "." as decimal sep supported by Utils.toNanoCoins
|
||||
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.ENGLISH);
|
||||
decimalFormat.setMaximumFractionDigits(10);
|
||||
decimalFormat.setMinimumFractionDigits(10);
|
||||
String stringValue = decimalFormat.format(value);
|
||||
return Utils.toNanoCoins(stringValue);
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.warn("Exception at doubleValueToSatoshis " + e.getMessage());
|
||||
return BigInteger.ZERO;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package io.bitsquare.btc;
|
||||
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
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 MS_TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; // Transaction.REFERENCE_DEFAULT_MIN_TX_FEE = BigInteger.valueOf(10000)
|
||||
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_CREATION_FEE = BigInteger.valueOf(7860); // Transaction.MIN_NONDUST_OUTPUT; // Utils.toNanoCoins("0.001");
|
||||
public static BigInteger OFFER_TAKER_FEE = BigInteger.valueOf(7860);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ 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.BitSquare;
|
||||
import io.bitsquare.crypto.CryptoFacade;
|
||||
import io.bitsquare.gui.util.Popups;
|
||||
import javafx.application.Platform;
|
||||
|
@ -35,16 +36,14 @@ public class WalletFacade implements WalletEventListener
|
|||
{
|
||||
public static final String MAIN_NET = "MAIN_NET";
|
||||
public static final String TEST_NET = "TEST_NET";
|
||||
public static final String REG_TEST_NET = "REG_TEST_NET";
|
||||
|
||||
public static String WALLET_PREFIX;
|
||||
public static String WALLET_PREFIX = BitSquare.ID;
|
||||
|
||||
// for testing trade process between offerer and taker
|
||||
//public static String WALLET_PREFIX = "offerer"; // offerer
|
||||
//public static String WALLET_PREFIX = "taker"; // offerer
|
||||
|
||||
//public static String WALLET_PREFIX = "bitsquare";
|
||||
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
|
||||
|
||||
private NetworkParameters params;
|
||||
|
@ -56,6 +55,8 @@ public class WalletFacade implements WalletEventListener
|
|||
|
||||
private List<DownloadListener> downloadListeners = new ArrayList<>();
|
||||
private List<WalletListener> walletListeners = new ArrayList<>();
|
||||
|
||||
|
||||
private Wallet wallet;
|
||||
|
||||
|
||||
|
@ -107,7 +108,7 @@ public class WalletFacade implements WalletEventListener
|
|||
|
||||
wallet = walletAppKit.wallet();
|
||||
|
||||
//wallet.allowSpendingUnconfirmedTransactions();
|
||||
wallet.allowSpendingUnconfirmedTransactions();
|
||||
walletAppKit.peerGroup().setMaxConnections(20);
|
||||
|
||||
wallet.addEventListener(this);
|
||||
|
@ -168,7 +169,7 @@ public class WalletFacade implements WalletEventListener
|
|||
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
|
||||
}
|
||||
|
||||
public String getAddress()
|
||||
public String getAddressAsString()
|
||||
{
|
||||
return wallet.getKeys().get(0).toAddress(params).toString();
|
||||
}
|
||||
|
@ -189,6 +190,18 @@ public class WalletFacade implements WalletEventListener
|
|||
return tx.getHashAsString();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade process
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int getNumOfPeersSeenTx(String txID)
|
||||
{
|
||||
// TODO check from blockchain
|
||||
// will be async
|
||||
return 3;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Account registration
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -200,7 +213,7 @@ public class WalletFacade implements WalletEventListener
|
|||
|
||||
public String getAccountRegistrationPubKey()
|
||||
{
|
||||
return Utils.bytesToHexString(getAccountRegistrationWallet().getKey().getPubKey());
|
||||
return Utils.bytesToHexString(getAccountRegistrationKey().getPubKey());
|
||||
}
|
||||
|
||||
public BigInteger getAccountRegistrationBalance()
|
||||
|
@ -213,6 +226,11 @@ public class WalletFacade implements WalletEventListener
|
|||
getAccountRegistrationWallet().saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(getAccountRegistrationWallet().getKey(), stringifiedBankAccounts));
|
||||
}
|
||||
|
||||
public ECKey getAccountRegistrationKey()
|
||||
{
|
||||
return getAccountRegistrationWallet().getKey();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -227,12 +245,11 @@ public class WalletFacade implements WalletEventListener
|
|||
return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet());
|
||||
}
|
||||
|
||||
public ECKey getAccountKey()
|
||||
public Wallet getWallet()
|
||||
{
|
||||
return getAccountRegistrationWallet().getKey();
|
||||
return wallet;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: WalletEventListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -243,7 +260,7 @@ public class WalletFacade implements WalletEventListener
|
|||
for (WalletListener walletListener : walletListeners)
|
||||
walletListener.onCoinsReceived(newBalance);
|
||||
|
||||
log.info("onCoinsReceived");
|
||||
// log.info("onCoinsReceived");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -252,13 +269,13 @@ public class WalletFacade implements WalletEventListener
|
|||
for (WalletListener walletListener : walletListeners)
|
||||
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(wallet));
|
||||
|
||||
log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
|
||||
// log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
log.info("onCoinsSent");
|
||||
// log.info("onCoinsSent");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -270,7 +287,7 @@ public class WalletFacade implements WalletEventListener
|
|||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
log.info("onWalletChanged");
|
||||
// log.info("onWalletChanged");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -285,12 +302,11 @@ public class WalletFacade implements WalletEventListener
|
|||
log.info("onScriptsAdded");
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private AccountRegistrationWallet getAccountRegistrationWallet()
|
||||
public AccountRegistrationWallet getAccountRegistrationWallet()
|
||||
{
|
||||
if (accountRegistrationWallet == null)
|
||||
accountRegistrationWallet = new AccountRegistrationWallet(params, walletAppKit.chain(), walletAppKit.peerGroup());
|
||||
|
@ -329,25 +345,25 @@ public class WalletFacade implements WalletEventListener
|
|||
String arbitratorPubKey = "";
|
||||
|
||||
// 1 offerer creates MS TX and pay in
|
||||
/* Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
|
||||
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);
|
||||
/* 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);
|
||||
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex, null);
|
||||
|
||||
log.info(tx3.toString()); // tx has 453 Bytes
|
||||
|
||||
|
@ -421,8 +437,13 @@ public class WalletFacade implements WalletEventListener
|
|||
|
||||
}
|
||||
|
||||
public String getMultiSigPubKeyAsHex()
|
||||
{
|
||||
return Utils.bytesToHexString(wallet.getKeys().get(0).getPubKey());
|
||||
}
|
||||
|
||||
// deposit 1. offerer
|
||||
private Transaction offererCreatesMSTxAndAddPayment(BigInteger offererAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
|
||||
public 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
|
||||
|
@ -494,13 +515,15 @@ public class WalletFacade implements WalletEventListener
|
|||
public Transaction offererSignAndSendTx(String tx1AsHex,
|
||||
String tx2AsHex,
|
||||
String tx2ConnOutAsHex,
|
||||
String tx2ScriptSigAsHex) throws Exception
|
||||
String tx2ScriptSigAsHex,
|
||||
FutureCallback<Transaction> callback) throws Exception
|
||||
{
|
||||
log.info("offererSignAndSendTx start");
|
||||
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);
|
||||
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 1, tx1ConnOut);
|
||||
TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint);
|
||||
tx1Input.setParent(tx);
|
||||
tx.addInput(tx1Input);
|
||||
|
@ -529,10 +552,13 @@ public class WalletFacade implements WalletEventListener
|
|||
else
|
||||
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
|
||||
|
||||
log.info("offererSignAndSendTx check correctlySpends input 0");
|
||||
input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
|
||||
|
||||
input = tx.getInput(1);
|
||||
scriptPubKey = input.getConnectedOutput().getScriptPubKey();
|
||||
|
||||
log.info("offererSignAndSendTx check correctlySpends input 1");
|
||||
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
|
||||
|
||||
/*
|
||||
|
@ -542,26 +568,28 @@ public class WalletFacade implements WalletEventListener
|
|||
OUT[1] offerer change
|
||||
OUT[2] taker change
|
||||
*/
|
||||
|
||||
log.info("offererSignAndSendTx broadcastTransaction verify ");
|
||||
tx.verify();
|
||||
|
||||
log.info("offererSignAndSendTx broadcastTransaction pre ");
|
||||
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
||||
|
||||
FutureCallback callback = new FutureCallback<Transaction>()
|
||||
log.info("offererSignAndSendTx broadcastTransaction post");
|
||||
FutureCallback<Transaction> localCallback = new FutureCallback<Transaction>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
{
|
||||
log.info("sendResult onSuccess:" + transaction.toString());
|
||||
log.info("offererSignAndSendTx onSuccess" + transaction.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.warn("sendResult onFailure:" + t.toString());
|
||||
log.info("offererSignAndSendTx onFailure" + t.toString());
|
||||
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
||||
}
|
||||
};
|
||||
Futures.addCallback(broadcastComplete, localCallback);
|
||||
Futures.addCallback(broadcastComplete, callback);
|
||||
|
||||
return tx;
|
||||
|
@ -628,7 +656,7 @@ public class WalletFacade implements WalletEventListener
|
|||
|
||||
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
||||
|
||||
FutureCallback callback = new FutureCallback<Transaction>()
|
||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
|
@ -648,7 +676,6 @@ public class WalletFacade implements WalletEventListener
|
|||
return tx;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Inner classes
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.google.common.base.Charsets;
|
|||
import com.google.inject.Inject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.security.SignatureException;
|
||||
import java.util.UUID;
|
||||
|
@ -58,7 +57,7 @@ public class CryptoFacade
|
|||
|
||||
public boolean verifyHash(String hashAsHexStringToVerify, String msg, String sig)
|
||||
{
|
||||
String hashAsHexString = Hex.toHexString(createHash(msg, sig));
|
||||
String hashAsHexString = Utils.bytesToHexString(createHash(msg, sig));
|
||||
return hashAsHexString.equals(hashAsHexStringToVerify);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ package io.bitsquare.di;
|
|||
import com.google.bitcoin.core.NetworkParameters;
|
||||
import com.google.bitcoin.kits.WalletAppKit;
|
||||
import com.google.bitcoin.params.MainNetParams;
|
||||
import com.google.bitcoin.params.RegTestParams;
|
||||
import com.google.bitcoin.params.TestNet3Params;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Inject;
|
||||
|
@ -17,10 +18,10 @@ import io.bitsquare.msg.MessageFacade;
|
|||
import io.bitsquare.settings.Settings;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.Trading;
|
||||
import io.bitsquare.trade.orderbook.MockOrderBook;
|
||||
import io.bitsquare.trade.orderbook.OrderBook;
|
||||
import io.bitsquare.trade.orderbook.OrderBookFilter;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
@ -31,7 +32,7 @@ public class BitSquareModule extends AbstractModule
|
|||
protected void configure()
|
||||
{
|
||||
bind(User.class).asEagerSingleton();
|
||||
bind(OrderBook.class).to(MockOrderBook.class).asEagerSingleton();
|
||||
bind(OrderBook.class).asEagerSingleton();
|
||||
bind(Storage.class).asEagerSingleton();
|
||||
bind(Settings.class).asEagerSingleton();
|
||||
bind(OrderBookFilter.class).asEagerSingleton();
|
||||
|
@ -44,6 +45,7 @@ public class BitSquareModule extends AbstractModule
|
|||
bind(Trading.class).asEagerSingleton();
|
||||
|
||||
//bind(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.MAIN_NET);
|
||||
// bind(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.REG_TEST_NET);
|
||||
bind(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.TEST_NET);
|
||||
bind(NetworkParameters.class).toProvider(NetworkParametersProvider.class).asEagerSingleton();
|
||||
bind(WalletAppKit.class).toProvider(WalletAppKitProvider.class).asEagerSingleton();
|
||||
|
@ -62,7 +64,7 @@ class WalletAppKitProvider implements Provider<WalletAppKit>
|
|||
|
||||
public WalletAppKit get()
|
||||
{
|
||||
return new WalletAppKit(networkParameters, new File("."), WalletFacade.WALLET_PREFIX);
|
||||
return new WalletAppKit(networkParameters, new File(Utilities.getRootDir()), WalletFacade.WALLET_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +90,9 @@ class NetworkParametersProvider implements Provider<NetworkParameters>
|
|||
case WalletFacade.TEST_NET:
|
||||
result = TestNet3Params.get();
|
||||
break;
|
||||
case WalletFacade.REG_TEST_NET:
|
||||
result = RegTestParams.get();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.bitsquare.gui;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.btc.BtcFormatter;
|
||||
|
@ -11,7 +13,9 @@ import io.bitsquare.gui.setup.SetupController;
|
|||
import io.bitsquare.gui.util.Icons;
|
||||
import io.bitsquare.gui.util.Localisation;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Trading;
|
||||
import io.bitsquare.user.User;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
@ -26,12 +30,15 @@ import javafx.scene.image.Image;
|
|||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.util.StringConverter;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class MainController implements Initializable, NavigationController, WalletFacade.DownloadListener
|
||||
|
@ -41,6 +48,7 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
private User user;
|
||||
private WalletFacade walletFacade;
|
||||
private MessageFacade messageFacade;
|
||||
private Trading trading;
|
||||
private ChildController childController;
|
||||
private ToggleGroup toggleGroup;
|
||||
private ToggleButton prevToggleButton;
|
||||
|
@ -48,6 +56,9 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
private Pane setupView;
|
||||
private SetupController setupController;
|
||||
private NetworkSyncPane networkSyncPane;
|
||||
private ToggleButton buyButton, sellButton, homeButton, msgButton, ordersButton, historyButton, fundsButton, settingsButton;
|
||||
private Pane msgButtonHolder, buyButtonHolder, sellButtonHolder, ordersButtonButtonHolder;
|
||||
private TextField balanceTextField;
|
||||
|
||||
@FXML
|
||||
public Pane contentPane;
|
||||
|
@ -64,11 +75,12 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public MainController(User user, WalletFacade walletFacade, MessageFacade messageFacade)
|
||||
public MainController(User user, WalletFacade walletFacade, MessageFacade messageFacade, Trading trading)
|
||||
{
|
||||
this.user = user;
|
||||
this.walletFacade = walletFacade;
|
||||
this.messageFacade = messageFacade;
|
||||
this.trading = trading;
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,19 +100,50 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
walletFacade.addDownloadListener(this);
|
||||
walletFacade.initWallet();
|
||||
|
||||
buildNavigation();
|
||||
if (user.getAccountID() == null)
|
||||
{
|
||||
buildSetupView();
|
||||
anchorPane.setOpacity(0);
|
||||
anchorPane.setVisible(false);
|
||||
setupController.setNetworkSyncPane(networkSyncPane);
|
||||
rootContainer.getChildren().add(setupView);
|
||||
}
|
||||
else
|
||||
{
|
||||
buildNavigation();
|
||||
|
||||
sellButton.fire();
|
||||
// ordersButton.fire();
|
||||
// homeButton.fire();
|
||||
// msgButton.fire();
|
||||
}
|
||||
|
||||
AnchorPane.setBottomAnchor(networkSyncPane, 0.0);
|
||||
AnchorPane.setLeftAnchor(networkSyncPane, 0.0);
|
||||
|
||||
messageFacade.addTakeOfferRequestListener((tradingMessage, sender) -> showTakeOfferRequest(tradingMessage, sender));
|
||||
}
|
||||
|
||||
private void showTakeOfferRequest(final TradeMessage tradeMessage, PeerAddress sender)
|
||||
{
|
||||
trading.createOffererPaymentProtocol(tradeMessage, sender);
|
||||
try
|
||||
{
|
||||
ImageView newTradeRequestIcon = Icons.getIconImageView(Icons.MSG_ALERT);
|
||||
Button alertButton = new Button("", newTradeRequestIcon);
|
||||
alertButton.setId("nav-alert-button");
|
||||
alertButton.relocate(36, 19);
|
||||
Tooltip.install(alertButton, new Tooltip("Someone accepted your offer"));
|
||||
|
||||
alertButton.setOnAction((e) -> {
|
||||
ordersButton.fire();
|
||||
});
|
||||
ordersButtonButtonHolder.getChildren().add(alertButton);
|
||||
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
log.warn("showTakeOfferRequest failed because of a NullPointerException");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: NavigationController
|
||||
|
@ -113,12 +156,12 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
{
|
||||
anchorPane.getChildren().add(networkSyncPane);
|
||||
|
||||
anchorPane.setOpacity(1);
|
||||
anchorPane.setVisible(true);
|
||||
rootContainer.getChildren().remove(setupView);
|
||||
setupView = null;
|
||||
setupController = null;
|
||||
|
||||
return null;
|
||||
buildNavigation();
|
||||
}
|
||||
|
||||
if (childController != null)
|
||||
|
@ -181,21 +224,31 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
{
|
||||
toggleGroup = new ToggleGroup();
|
||||
|
||||
ToggleButton homeButton = addNavButton(leftNavPane, "Overview", Icons.HOME, Icons.HOME, NavigationController.HOME);
|
||||
ToggleButton buyButton = addNavButton(leftNavPane, "Buy BTC", Icons.NAV_BUY, Icons.NAV_BUY_ACTIVE, NavigationController.MARKET, Direction.BUY);
|
||||
ToggleButton sellButton = addNavButton(leftNavPane, "Sell BTC", Icons.NAV_SELL, Icons.NAV_SELL_ACTIVE, NavigationController.MARKET, Direction.SELL);
|
||||
addNavButton(leftNavPane, "Orders", Icons.ORDERS, Icons.ORDERS, NavigationController.ORDERS);
|
||||
addNavButton(leftNavPane, "History", Icons.HISTORY, Icons.HISTORY, NavigationController.HISTORY);
|
||||
addNavButton(leftNavPane, "Funds", Icons.FUNDS, Icons.FUNDS, NavigationController.FUNDS);
|
||||
ToggleButton msgButton = addNavButton(leftNavPane, "Message", Icons.MSG, Icons.MSG, NavigationController.MSG);
|
||||
homeButton = addNavButton(leftNavPane, "Overview", Icons.HOME, Icons.HOME, NavigationController.HOME);
|
||||
|
||||
buyButtonHolder = new Pane();
|
||||
buyButton = addNavButton(buyButtonHolder, "Buy BTC", Icons.NAV_BUY, Icons.NAV_BUY_ACTIVE, NavigationController.MARKET, Direction.BUY);
|
||||
leftNavPane.getChildren().add(buyButtonHolder);
|
||||
|
||||
sellButtonHolder = new Pane();
|
||||
sellButton = addNavButton(sellButtonHolder, "Sell BTC", Icons.NAV_SELL, Icons.NAV_SELL_ACTIVE, NavigationController.MARKET, Direction.SELL);
|
||||
leftNavPane.getChildren().add(sellButtonHolder);
|
||||
|
||||
ordersButtonButtonHolder = new Pane();
|
||||
ordersButton = addNavButton(ordersButtonButtonHolder, "Orders", Icons.ORDERS, Icons.ORDERS, NavigationController.ORDERS);
|
||||
leftNavPane.getChildren().add(ordersButtonButtonHolder);
|
||||
|
||||
historyButton = addNavButton(leftNavPane, "History", Icons.HISTORY, Icons.HISTORY, NavigationController.HISTORY);
|
||||
fundsButton = addNavButton(leftNavPane, "Funds", Icons.FUNDS, Icons.FUNDS, NavigationController.FUNDS);
|
||||
|
||||
msgButtonHolder = new Pane();
|
||||
msgButton = addNavButton(msgButtonHolder, "Message", Icons.MSG, Icons.MSG, NavigationController.MSG);
|
||||
leftNavPane.getChildren().add(msgButtonHolder);
|
||||
|
||||
addBalanceInfo(rightNavPane);
|
||||
addAccountComboBox(rightNavPane);
|
||||
|
||||
addNavButton(rightNavPane, "Settings", Icons.SETTINGS, Icons.SETTINGS, NavigationController.SETTINGS);
|
||||
|
||||
//sellButton.fire();
|
||||
//homeButton.fire();
|
||||
msgButton.fire();
|
||||
settingsButton = addNavButton(rightNavPane, "Settings", Icons.SETTINGS, Icons.SETTINGS, NavigationController.SETTINGS);
|
||||
}
|
||||
|
||||
private ToggleButton addNavButton(Pane parent, String title, String iconId, String iconIdActivated, String navTarget)
|
||||
|
@ -250,18 +303,19 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
|
||||
private TextField addBalanceInfo(Pane parent)
|
||||
{
|
||||
TextField balanceLabel = new TextField();
|
||||
balanceLabel.setEditable(false);
|
||||
balanceLabel.setMouseTransparent(true);
|
||||
balanceLabel.setPrefWidth(90);
|
||||
balanceLabel.setId("nav-balance-label");
|
||||
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
|
||||
balanceTextField = new TextField();
|
||||
balanceTextField.setEditable(false);
|
||||
balanceTextField.setMouseTransparent(true);
|
||||
balanceTextField.setPrefWidth(90);
|
||||
balanceTextField.setId("nav-balance-label");
|
||||
|
||||
balanceTextField.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
|
||||
|
||||
Label balanceCurrencyLabel = new Label("BTC");
|
||||
balanceCurrencyLabel.setPadding(new Insets(6, 0, 0, 0));
|
||||
HBox hBox = new HBox();
|
||||
hBox.setSpacing(2);
|
||||
hBox.getChildren().setAll(balanceLabel, balanceCurrencyLabel);
|
||||
hBox.getChildren().setAll(balanceTextField, balanceCurrencyLabel);
|
||||
|
||||
VBox vBox = new VBox();
|
||||
vBox.setPadding(new Insets(12, 0, 0, 0));
|
||||
|
@ -274,7 +328,47 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
vBox.getChildren().setAll(hBox, titleLabel);
|
||||
parent.getChildren().add(vBox);
|
||||
|
||||
return balanceLabel;
|
||||
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(walletFacade.getBalance()));
|
||||
walletFacade.getWallet().addEventListener(new WalletEventListener()
|
||||
{
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(newBalance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(Wallet wallet, List<ECKey> keys)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsAdded(Wallet wallet, List<Script> scripts)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
return balanceTextField;
|
||||
}
|
||||
|
||||
private void addAccountComboBox(Pane parent)
|
||||
|
@ -322,4 +416,5 @@ public class MainController implements Initializable, NavigationController, Wall
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -13,8 +13,9 @@ public interface NavigationController
|
|||
public static final String SETTINGS = "/io/bitsquare/gui/settings/SettingsView.fxml";
|
||||
|
||||
public static final String ORDER_BOOK = "/io/bitsquare/gui/market/orderbook/OrderBookView.fxml";
|
||||
public static final String TRADE = "/io/bitsquare/gui/market/trade/TradeView.fxml";
|
||||
public static final String CREATE_OFFER = "/io/bitsquare/gui/market/offer/CreateOfferView.fxml";
|
||||
public static final String TAKER_TRADE = "/io/bitsquare/gui/market/trade/TakerTradeView.fxml";
|
||||
public static final String OFFERER_TRADE = "/io/bitsquare/gui/orders/OffererTradeView.fxml";
|
||||
public static final String CREATE_OFFER = "/io/bitsquare/gui/market/createOffer/CreateOfferView.fxml";
|
||||
|
||||
ChildController navigateToView(String fxmlView, String title);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.bitsquare.gui.components.processbar;
|
||||
|
||||
import io.bitsquare.util.Utils;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Control;
|
||||
|
@ -65,7 +65,7 @@ public class ProcessStepsBuilder
|
|||
|
||||
// TODO
|
||||
// mock simulate network delay
|
||||
Utils.setTimeout(100, (AnimationTimer animationTimer) -> {
|
||||
Utilities.setTimeout(100, (AnimationTimer animationTimer) -> {
|
||||
next();
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -1,39 +1,43 @@
|
|||
package io.bitsquare.gui.funds;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
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 javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.layout.Pane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class FundsController implements Initializable, ChildController
|
||||
{
|
||||
|
||||
private NavigationController navigationController;
|
||||
private static final Logger log = LoggerFactory.getLogger(FundsController.class);
|
||||
private WalletFacade walletFacade;
|
||||
|
||||
@FXML
|
||||
public Pane rootContainer;
|
||||
private TextField tradingAccountTextField, balanceTextField;
|
||||
@FXML
|
||||
public TextField addressLabel;
|
||||
private Label copyIcon, confirmationLabel;
|
||||
@FXML
|
||||
public TextField balanceLabel;
|
||||
@FXML
|
||||
public Label copyIcon;
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
@Inject
|
||||
public FundsController(WalletFacade walletFacade)
|
||||
|
@ -46,24 +50,65 @@ public class FundsController implements Initializable, ChildController
|
|||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
String tradingAccountAddress = walletFacade.getAddressAsString();
|
||||
tradingAccountTextField.setText(tradingAccountAddress);
|
||||
|
||||
copyIcon.setId("copy-icon");
|
||||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard"));
|
||||
|
||||
copyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(addressLabel.getText());
|
||||
content.putString(tradingAccountAddress);
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
|
||||
updateBalance(walletFacade.getBalance());
|
||||
|
||||
walletFacade.getWallet().addEventListener(new WalletEventListener()
|
||||
{
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
updateBalance(newBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
|
||||
{
|
||||
updateConfidence(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(Wallet wallet, List<ECKey> keys)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsAdded(Wallet wallet, List<Script> scripts)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationController(NavigationController navigationController)
|
||||
{
|
||||
this.navigationController = navigationController;
|
||||
|
||||
addressLabel.setText(walletFacade.getAddress());
|
||||
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getBalance(), false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,6 +117,72 @@ public class FundsController implements Initializable, ChildController
|
|||
|
||||
}
|
||||
|
||||
private void updateBalance(BigInteger balance)
|
||||
{
|
||||
if (balance.compareTo(BigInteger.ZERO) == 0)
|
||||
{
|
||||
confirmationLabel.setText("");
|
||||
progressIndicator.setOpacity(0);
|
||||
progressIndicator.setProgress(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
progressIndicator.setOpacity(1);
|
||||
progressIndicator.setProgress(-1);
|
||||
Set<Transaction> transactions = walletFacade.getWallet().getTransactions(false);
|
||||
Transaction latestTransaction = null;
|
||||
for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
Transaction transaction = iterator.next();
|
||||
if (latestTransaction != null)
|
||||
{
|
||||
if (transaction.getUpdateTime().compareTo(latestTransaction.getUpdateTime()) > 0)
|
||||
{
|
||||
latestTransaction = transaction;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
latestTransaction = transaction;
|
||||
}
|
||||
}
|
||||
if (latestTransaction != null)
|
||||
{
|
||||
updateConfidence(latestTransaction);
|
||||
}
|
||||
}
|
||||
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(balance));
|
||||
}
|
||||
|
||||
private void updateConfidence(Transaction tx)
|
||||
{
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
double progressIndicatorSize = 50;
|
||||
switch (confidence.getConfidenceType())
|
||||
{
|
||||
case UNKNOWN:
|
||||
confirmationLabel.setText("");
|
||||
progressIndicator.setProgress(0);
|
||||
break;
|
||||
case PENDING:
|
||||
confirmationLabel.setText("Seen by " + confidence.numBroadcastPeers() + " peer(s) / 0 confirmations");
|
||||
progressIndicator.setProgress(-1.0);
|
||||
progressIndicatorSize = 20;
|
||||
break;
|
||||
case BUILDING:
|
||||
confirmationLabel.setText("Confirmed in " + confidence.getDepthInBlocks() + " block(s)");
|
||||
progressIndicator.setProgress(Math.min(1, (double) confidence.getDepthInBlocks() / 6.0));
|
||||
break;
|
||||
case DEAD:
|
||||
confirmationLabel.setText("Transaction is invalid.");
|
||||
break;
|
||||
}
|
||||
|
||||
progressIndicator.setMaxHeight(progressIndicatorSize);
|
||||
progressIndicator.setPrefHeight(progressIndicatorSize);
|
||||
progressIndicator.setMaxWidth(progressIndicatorSize);
|
||||
progressIndicator.setPrefWidth(progressIndicatorSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,57 @@
|
|||
<?import io.bitsquare.gui.components.HSpacer?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:controller="io.bitsquare.gui.funds.FundsController"
|
||||
xmlns:fx="http://javafx.com/fxml" fx:id="rootContainer">
|
||||
<AnchorPane fx:controller="io.bitsquare.gui.funds.FundsController" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label text="Just one generic address by now for dev...." AnchorPane.topAnchor="10" AnchorPane.leftAnchor="10"/>
|
||||
<Label id="headline-label" text="Trading account wallet"/>
|
||||
|
||||
<HBox spacing="5" AnchorPane.topAnchor="40" AnchorPane.leftAnchor="10">
|
||||
<Label text="Funds address:">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<TextField fx:id="addressLabel" prefWidth="300" editable="false"/>
|
||||
<GridPane hgap="5.0" vgap="5.0">
|
||||
|
||||
<Label fx:id="copyIcon">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="4.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<HSpacer prefWidth="10"/>
|
||||
<children>
|
||||
<Label text="Trading account address:"/>
|
||||
<TextField fx:id="tradingAccountTextField" editable="false" GridPane.columnIndex="1"/>
|
||||
<Label fx:id="copyIcon" minWidth="10" GridPane.columnIndex="2">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<Label text="Balance:">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
<TextField fx:id="balanceLabel" prefWidth="100" editable="false"/>
|
||||
<Label text="BTC">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="5.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
</HBox>
|
||||
<Label text="Balance:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="balanceTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<ProgressIndicator fx:id="progressIndicator" id="confidence-progress-indicator" GridPane.columnIndex="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="1" GridPane.rowSpan="2" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="2.0"/>
|
||||
</GridPane.margin>
|
||||
</ProgressIndicator>
|
||||
<Label fx:id="confirmationLabel" text="Checking confirmations..." GridPane.columnIndex="3" GridPane.rowIndex="1"/>
|
||||
|
||||
<Label text="dummy for layout progressIndicator" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
|
||||
</children>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" prefWidth="20.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
|
@ -15,6 +15,18 @@
|
|||
-fx-font-size: 14;
|
||||
}
|
||||
|
||||
#online-label {
|
||||
-fx-fill: green;
|
||||
}
|
||||
|
||||
#offline-label {
|
||||
-fx-fill: red;
|
||||
}
|
||||
|
||||
#confidence-progress-indicator {
|
||||
-fx-fill: null;
|
||||
}
|
||||
|
||||
/* main nav */
|
||||
|
||||
#nav-button {
|
||||
|
@ -22,6 +34,12 @@
|
|||
-fx-alignment: center;
|
||||
}
|
||||
|
||||
#nav-alert-button {
|
||||
-fx-background-color: transparent;
|
||||
-fx-cursor: hand;
|
||||
-fx-border-style: none;
|
||||
}
|
||||
|
||||
#nav-button-label {
|
||||
-fx-font-size: 10;
|
||||
-fx-text-alignment: center;
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<Pane fx:controller="io.bitsquare.gui.history.HistoryController"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Label text="Histroy"/>
|
||||
</Pane>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:controller="io.bitsquare.gui.history.HistoryController" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label id="headline-label" text="Trade history"/>
|
||||
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
|
@ -1,6 +1,14 @@
|
|||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="rootContainer" fx:controller="io.bitsquare.gui.home.HomeController" spacing="10"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Label text="Overview"/>
|
||||
</VBox>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:controller="io.bitsquare.gui.home.HomeController" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label id="headline-label" text="Overview"/>
|
||||
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
|
@ -0,0 +1,355 @@
|
|||
package io.bitsquare.gui.market.createOffer;
|
||||
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
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;
|
||||
import io.bitsquare.gui.NavigationController;
|
||||
import io.bitsquare.gui.util.Converter;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import io.bitsquare.gui.util.Localisation;
|
||||
import io.bitsquare.gui.util.Popups;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.settings.Settings;
|
||||
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;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class CreateOfferController implements Initializable, ChildController
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferController.class);
|
||||
|
||||
private NavigationController navigationController;
|
||||
private Trading trading;
|
||||
private WalletFacade walletFacade;
|
||||
private MessageFacade messageFacade;
|
||||
private Settings settings;
|
||||
private User user;
|
||||
private Direction direction;
|
||||
private Offer offer;
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootContainer;
|
||||
@FXML
|
||||
private Label buyLabel, placeOfferTitle, confirmationLabel, txTitleLabel;
|
||||
@FXML
|
||||
private TextField volumeTextField, amountTextField, priceTextField;
|
||||
@FXML
|
||||
private Button placeOfferButton, closeButton;
|
||||
@FXML
|
||||
private TextField collateralTextField, minAmountTextField, bankAccountTypeTextField, bankAccountCurrencyTextField, bankAccountCountyTextField,
|
||||
acceptedCountriesTextField, acceptedLanguagesTextField, feeLabel, txTextField;
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public CreateOfferController(Trading trading, WalletFacade walletFacade, MessageFacade messageFacade, Settings settings, User user)
|
||||
{
|
||||
this.trading = trading;
|
||||
this.walletFacade = walletFacade;
|
||||
this.messageFacade = messageFacade;
|
||||
this.settings = settings;
|
||||
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();
|
||||
|
||||
|
||||
//TODO
|
||||
amountTextField.setText("0,01");
|
||||
minAmountTextField.setText("0,001");
|
||||
priceTextField.setText("500");
|
||||
updateVolume();
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: Initializable
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
bankAccountTypeTextField.setText(Localisation.get(user.getCurrentBankAccount().getBankAccountType().getType().toString()));
|
||||
bankAccountCurrencyTextField.setText(user.getCurrentBankAccount().getCurrency().getCurrencyCode());
|
||||
bankAccountCountyTextField.setText(user.getCurrentBankAccount().getCountryLocale().getDisplayCountry());
|
||||
acceptedCountriesTextField.setText(Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()));
|
||||
acceptedLanguagesTextField.setText(Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()));
|
||||
feeLabel.setText(Utils.bitcoinValueToFriendlyString(Fees.OFFER_CREATION_FEE));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: ChildController
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setNavigationController(NavigationController navigationController)
|
||||
{
|
||||
this.navigationController = navigationController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI Handlers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onPlaceOffer(ActionEvent actionEvent)
|
||||
{
|
||||
if (!inputValid())
|
||||
{
|
||||
Popups.openWarningPopup("Invalid input", "Your input is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
int collateral = (int) (Converter.stringToDouble(collateralTextField.getText()));
|
||||
Arbitrator arbitrator = settings.getRandomArbitrator(collateral, getAmountAsBI());
|
||||
if (arbitrator == null)
|
||||
{
|
||||
Popups.openWarningPopup("No arbitrator available", "No arbitrator from your arbitrator list does match the collateral and amount value.");
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("create offer pubkey " + user.getMessagePubKeyAsHex());
|
||||
|
||||
offer = new Offer(user.getMessagePubKeyAsHex(),
|
||||
direction,
|
||||
Converter.stringToDouble(priceTextField.getText()),
|
||||
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
|
||||
BtcFormatter.stringValueToSatoshis(minAmountTextField.getText()),
|
||||
user.getCurrentBankAccount().getBankAccountType().getType(),
|
||||
user.getCurrentBankAccount().getCurrency(),
|
||||
user.getCurrentBankAccount().getCountryLocale(),
|
||||
user.getCurrentBankAccount().getUid(),
|
||||
arbitrator,
|
||||
collateral,
|
||||
settings.getAcceptedCountryLocales(),
|
||||
settings.getAcceptedLanguageLocales());
|
||||
|
||||
FutureCallback callback = new FutureCallback<Transaction>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
{
|
||||
log.info("sendResult onSuccess:" + transaction.toString());
|
||||
offer.setOfferFeePaymentTxID(transaction.getHashAsString());
|
||||
setupSuccessScreen(transaction);
|
||||
placeOfferTitle.setText("Transaction sent:");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.warn("sendResult onFailure:" + t.toString());
|
||||
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
||||
placeOfferButton.setDisable(false);
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
trading.placeNewOffer(offer, callback);
|
||||
messageFacade.addOffer(offer);
|
||||
placeOfferButton.setDisable(true);
|
||||
} catch (InsufficientMoneyException e1)
|
||||
{
|
||||
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet. " + e1.getMessage());
|
||||
} catch (IOException e1)
|
||||
{
|
||||
Popups.openErrorPopup("Could not publish offer", "Could not publish offer. " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void onClose(ActionEvent actionEvent)
|
||||
{
|
||||
TabPane tabPane = ((TabPane) (rootContainer.getParent().getParent()));
|
||||
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
|
||||
|
||||
navigationController.navigateToView(NavigationController.ORDER_BOOK, "Orderbook");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setupSuccessScreen(Transaction newTransaction)
|
||||
{
|
||||
placeOfferButton.setVisible(false);
|
||||
|
||||
progressIndicator.setVisible(true);
|
||||
confirmationLabel.setVisible(true);
|
||||
txTitleLabel.setVisible(true);
|
||||
txTextField.setVisible(true);
|
||||
closeButton.setVisible(true);
|
||||
|
||||
txTextField.setText(newTransaction.getHashAsString());
|
||||
|
||||
updateConfidence(newTransaction);
|
||||
|
||||
walletFacade.getWallet().addEventListener(new WalletEventListener()
|
||||
{
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
|
||||
{
|
||||
updateConfidence(newTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(Wallet wallet, List<ECKey> keys)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsAdded(Wallet wallet, List<Script> scripts)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConfidence(Transaction tx)
|
||||
{
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
double progressIndicatorSize = 20;
|
||||
switch (confidence.getConfidenceType())
|
||||
{
|
||||
case UNKNOWN:
|
||||
confirmationLabel.setText("");
|
||||
progressIndicator.setProgress(0);
|
||||
progressIndicatorSize = 50;
|
||||
break;
|
||||
case PENDING:
|
||||
confirmationLabel.setText("Seen by " + confidence.numBroadcastPeers() + " peer(s) / 0 confirmations");
|
||||
progressIndicator.setProgress(-1.0);
|
||||
break;
|
||||
case BUILDING:
|
||||
confirmationLabel.setText("Confirmed in " + confidence.getDepthInBlocks() + " block(s)");
|
||||
progressIndicator.setProgress(Math.min(1, (double) confidence.getDepthInBlocks() / 6.0));
|
||||
progressIndicatorSize = 50;
|
||||
break;
|
||||
case DEAD:
|
||||
confirmationLabel.setText("Transaction is invalid.");
|
||||
break;
|
||||
}
|
||||
|
||||
progressIndicator.setMaxHeight(progressIndicatorSize);
|
||||
progressIndicator.setPrefHeight(progressIndicatorSize);
|
||||
progressIndicator.setMaxWidth(progressIndicatorSize);
|
||||
progressIndicator.setPrefWidth(progressIndicatorSize);
|
||||
}
|
||||
|
||||
private void updateVolume()
|
||||
{
|
||||
volumeTextField.setText(Formatter.formatVolume(getVolume()));
|
||||
}
|
||||
|
||||
private double getVolume()
|
||||
{
|
||||
double amountAsDouble = Converter.stringToDouble(amountTextField.getText());
|
||||
double priceAsDouble = Converter.stringToDouble(priceTextField.getText());
|
||||
return amountAsDouble * priceAsDouble;
|
||||
}
|
||||
|
||||
private BigInteger getAmountAsBI()
|
||||
{
|
||||
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
|
||||
}
|
||||
|
||||
private boolean inputValid()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="rootContainer" prefHeight="500" prefWidth="800" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="io.bitsquare.gui.market.createOffer.CreateOfferController">
|
||||
<children>
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
|
||||
<children>
|
||||
<Label id="form-header-text" text="Create new offer" GridPane.rowIndex="0"/>
|
||||
|
||||
<Label fx:id="buyLabel" GridPane.rowIndex="1"/>
|
||||
|
||||
<HBox spacing="5" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" alignment="CENTER_LEFT">
|
||||
<TextField fx:id="amountTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
|
||||
<Label text="BTC for:"/>
|
||||
<TextField fx:id="priceTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
|
||||
<Label text="EUR ="/>
|
||||
<TextField fx:id="volumeTextField" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"/>
|
||||
<Label text="EUR in total"/>
|
||||
</HBox>
|
||||
|
||||
<Label text="Min. Amount:" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="minAmountTextField" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
|
||||
<Label text="Collateral (%):" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="collateralTextField" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
|
||||
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="4">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
|
||||
<Label text="Bank account type:" GridPane.rowIndex="5"/>
|
||||
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
|
||||
|
||||
<Label text="Bank account currency:" GridPane.rowIndex="6"/>
|
||||
<TextField fx:id="bankAccountCurrencyTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="6"/>
|
||||
|
||||
<Label text="Bank account county:" GridPane.rowIndex="7"/>
|
||||
<TextField fx:id="bankAccountCountyTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||
|
||||
<Label text="Accepted countries:" GridPane.rowIndex="8"/>
|
||||
<TextField fx:id="acceptedCountriesTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="8"/>
|
||||
|
||||
<Label text="Accepted languages:" GridPane.rowIndex="9"/>
|
||||
<TextField fx:id="acceptedLanguagesTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
|
||||
|
||||
|
||||
<Label id="form-header-text" fx:id="placeOfferTitle" text="Place offer:" GridPane.rowIndex="10">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
|
||||
<Label text="Offer fee:" GridPane.rowIndex="11"/>
|
||||
<TextField fx:id="feeLabel" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="11"/>
|
||||
|
||||
<Button fx:id="placeOfferButton" defaultButton="true" onAction="#onPlaceOffer" text="Place offer" GridPane.columnIndex="1" GridPane.rowIndex="12"/>
|
||||
|
||||
<Label fx:id="txTitleLabel" text="Transaction ID:" visible="false" GridPane.rowIndex="12"/>
|
||||
<TextField fx:id="txTextField" visible="false" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="12"/>
|
||||
<ProgressIndicator fx:id="progressIndicator" id="confidence-progress-indicator" visible="false" progress="0" GridPane.columnIndex="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="12"
|
||||
GridPane.rowSpan="2" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="2.0"/>
|
||||
</GridPane.margin>
|
||||
</ProgressIndicator>
|
||||
<Label fx:id="confirmationLabel" visible="false" text="Checking confirmations..." GridPane.columnIndex="3" GridPane.rowIndex="12"/>
|
||||
|
||||
<Button fx:id="closeButton" visible="false" defaultButton="true" onAction="#onClose" text="Close" GridPane.columnIndex="1" GridPane.rowIndex="13"/>
|
||||
|
||||
</children>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="400"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" prefWidth="20.0" minWidth="20"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
<opaqueInsets>
|
||||
<Insets/>
|
||||
</opaqueInsets>
|
||||
|
||||
</GridPane>
|
||||
</children>
|
||||
</AnchorPane>
|
|
@ -1,299 +0,0 @@
|
|||
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;
|
||||
import io.bitsquare.gui.NavigationController;
|
||||
import io.bitsquare.gui.components.ConfirmationComponent;
|
||||
import io.bitsquare.gui.util.*;
|
||||
import io.bitsquare.settings.Settings;
|
||||
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;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TabPane;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class CreateOfferController implements Initializable, ChildController, WalletFacade.WalletListener
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferController.class);
|
||||
|
||||
private NavigationController navigationController;
|
||||
private Trading trading;
|
||||
private WalletFacade walletFacade;
|
||||
private Settings settings;
|
||||
private User user;
|
||||
private Direction direction;
|
||||
private Offer offer;
|
||||
private int gridRow;
|
||||
|
||||
private Button placeOfferButton;
|
||||
private TextField collateralTextField, minAmountTextField;
|
||||
|
||||
@FXML
|
||||
private AnchorPane holderPane;
|
||||
@FXML
|
||||
private GridPane formGridPane;
|
||||
@FXML
|
||||
public Label buyLabel;
|
||||
@FXML
|
||||
public TextField volumeTextField, amountTextField, priceTextField;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public CreateOfferController(Trading trading, WalletFacade walletFacade, Settings settings, User user)
|
||||
{
|
||||
this.trading = trading;
|
||||
this.walletFacade = walletFacade;
|
||||
this.settings = settings;
|
||||
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);
|
||||
|
||||
buildScreen();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: ChildController
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setNavigationController(NavigationController navigationController)
|
||||
{
|
||||
this.navigationController = navigationController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: WalletFacade.WalletListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks)
|
||||
{
|
||||
log.info("onConfidenceChanged " + numBroadcastPeers + " / " + depthInBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsReceived(BigInteger 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)
|
||||
{
|
||||
FormBuilder.addTextField(formGridPane, "Transaction ID:", txID, ++gridRow, false, true);
|
||||
|
||||
new ConfirmationComponent(walletFacade, formGridPane, ++gridRow);
|
||||
|
||||
Button closeButton = new Button("Close");
|
||||
formGridPane.add(closeButton, 1, ++gridRow);
|
||||
closeButton.setDefaultButton(true);
|
||||
|
||||
|
||||
closeButton.setOnAction(e -> {
|
||||
TabPane tabPane = ((TabPane) (holderPane.getParent().getParent()));
|
||||
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
|
||||
|
||||
navigationController.navigateToView(NavigationController.ORDER_BOOK, "Orderbook");
|
||||
});
|
||||
}
|
||||
|
||||
private boolean inputValid()
|
||||
{
|
||||
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()
|
||||
{
|
||||
volumeTextField.setText(Formatter.formatVolume(getVolume()));
|
||||
}
|
||||
|
||||
private double getVolume()
|
||||
{
|
||||
double amountAsDouble = Converter.stringToDouble(amountTextField.getText());
|
||||
double priceAsDouble = Converter.stringToDouble(priceTextField.getText());
|
||||
return amountAsDouble * priceAsDouble;
|
||||
}
|
||||
|
||||
private BigInteger getAmountAsBI()
|
||||
{
|
||||
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="holderPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="io.bitsquare.gui.market.offer.CreateOfferController">
|
||||
<VBox spacing="10" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="5.0"
|
||||
AnchorPane.bottomAnchor="10.0">
|
||||
<GridPane fx:id="formGridPane" vgap="5" hgap="5">
|
||||
<Label id="form-header-text" text="Create new offer:" GridPane.rowIndex="0"
|
||||
GridPane.columnIndex="0"/>
|
||||
|
||||
<Label fx:id="buyLabel" text="Buy" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
|
||||
|
||||
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.hgrow="NEVER" spacing="5"
|
||||
alignment="CENTER_RIGHT">
|
||||
<TextField fx:id="amountTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
|
||||
<Label text="BTC for:"/>
|
||||
<TextField fx:id="priceTextField" prefWidth="70.0" alignment="CENTER_RIGHT"/>
|
||||
<Label text="EUR ="/>
|
||||
<TextField fx:id="volumeTextField" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"
|
||||
mouseTransparent="true"/>
|
||||
<Label text="EUR in total"/>
|
||||
</HBox>
|
||||
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
|
||||
</padding>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT"/>
|
||||
<ColumnConstraints halignment="LEFT"/>
|
||||
<ColumnConstraints halignment="LEFT"/>
|
||||
</columnConstraints>
|
||||
</GridPane>
|
||||
</VBox>
|
||||
</AnchorPane>
|
|
@ -5,16 +5,20 @@ 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;
|
||||
import io.bitsquare.gui.market.trade.TradeController;
|
||||
import io.bitsquare.gui.market.createOffer.CreateOfferController;
|
||||
import io.bitsquare.gui.market.trade.TakerTradeController;
|
||||
import io.bitsquare.gui.util.Converter;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import io.bitsquare.gui.util.Icons;
|
||||
import io.bitsquare.gui.util.Localisation;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.orderbook.OrderBook;
|
||||
import io.bitsquare.trade.orderbook.OrderBookFilter;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
@ -31,6 +35,7 @@ import javafx.util.Callback;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
|
@ -47,7 +52,8 @@ public class OrderBookController implements Initializable, ChildController
|
|||
private SortedList<OrderBookListItem> offerList;
|
||||
private final OrderBookFilter orderBookFilter;
|
||||
private User user;
|
||||
|
||||
private MessageFacade messageFacade;
|
||||
private AnimationTimer pollingTimer;
|
||||
|
||||
private Image buyIcon = Icons.getIconImage(Icons.BUY);
|
||||
private Image sellIcon = Icons.getIconImage(Icons.SELL);
|
||||
|
@ -67,17 +73,30 @@ public class OrderBookController implements Initializable, ChildController
|
|||
@FXML
|
||||
public Button createOfferButton;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OrderBookController(OrderBook orderBook, OrderBookFilter orderBookFilter, User user)
|
||||
public OrderBookController(OrderBook orderBook, OrderBookFilter orderBookFilter, User user, MessageFacade messageFacade)
|
||||
{
|
||||
this.orderBook = orderBook;
|
||||
this.orderBookFilter = orderBookFilter;
|
||||
this.user = user;
|
||||
this.messageFacade = messageFacade;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: Initializable
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
orderBook.init();
|
||||
|
||||
// setup table
|
||||
setCountryColumnCellFactory();
|
||||
setBankAccountTypeColumnCellFactory();
|
||||
|
@ -88,6 +107,8 @@ public class OrderBookController implements Initializable, ChildController
|
|||
orderBookTable.getSortOrder().add(priceColumn);
|
||||
orderBookTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
|
||||
orderBook.loadOffers();
|
||||
|
||||
// handlers
|
||||
amount.textProperty().addListener(new ChangeListener<String>()
|
||||
{
|
||||
|
@ -109,20 +130,21 @@ public class OrderBookController implements Initializable, ChildController
|
|||
}
|
||||
});
|
||||
|
||||
orderBookFilter.getChangedProperty().addListener(new ChangeListener<Boolean>()
|
||||
orderBookFilter.getDirectionChangedProperty().addListener(new ChangeListener<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
|
||||
{
|
||||
updateOfferList();
|
||||
applyOffers();
|
||||
}
|
||||
});
|
||||
user.getChangedProperty().addListener(new ChangeListener<Boolean>()
|
||||
|
||||
user.getBankAccountChangedProperty().addListener(new ChangeListener<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
|
||||
{
|
||||
updateOfferList();
|
||||
orderBook.loadOffers();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -130,8 +152,16 @@ public class OrderBookController implements Initializable, ChildController
|
|||
ChildController nextController = navigationController.navigateToView(NavigationController.CREATE_OFFER, "Create offer");
|
||||
((CreateOfferController) nextController).setOrderBookFilter(orderBookFilter);
|
||||
});
|
||||
|
||||
//TODO do polling until broadcast works
|
||||
setupPolling();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: ChildController
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setNavigationController(NavigationController navigationController)
|
||||
{
|
||||
|
@ -141,12 +171,24 @@ public class OrderBookController implements Initializable, ChildController
|
|||
@Override
|
||||
public void cleanup()
|
||||
{
|
||||
orderBook.cleanup();
|
||||
|
||||
orderBookTable.setItems(null);
|
||||
orderBookTable.getSortOrder().clear();
|
||||
offerList.comparatorProperty().unbind();
|
||||
|
||||
if (pollingTimer != null)
|
||||
{
|
||||
pollingTimer.stop();
|
||||
pollingTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setDirection(Direction direction)
|
||||
{
|
||||
orderBookTable.getSelectionModel().clearSelection();
|
||||
|
@ -155,21 +197,36 @@ public class OrderBookController implements Initializable, ChildController
|
|||
}
|
||||
|
||||
|
||||
private void openTradeTab(OrderBookListItem orderBookListItem)
|
||||
{
|
||||
String title = orderBookListItem.getOffer().getDirection() == Direction.BUY ? "Trade: Sell Bitcoin" : "Trade: Buy Bitcoin";
|
||||
TradeController tradeController = (TradeController) navigationController.navigateToView(NavigationController.TRADE, title);
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BigInteger requestedAmount = orderBookListItem.getOffer().getAmount();
|
||||
private void takeOffer(Offer offer)
|
||||
{
|
||||
String title = offer.getDirection() == Direction.BUY ? "Trade: Sell Bitcoin" : "Trade: Buy Bitcoin";
|
||||
TakerTradeController takerTradeController = (TakerTradeController) navigationController.navigateToView(NavigationController.TAKER_TRADE, title);
|
||||
|
||||
BigInteger requestedAmount = offer.getAmount();
|
||||
if (!amount.getText().equals(""))
|
||||
requestedAmount = BtcFormatter.stringValueToSatoshis(amount.getText());
|
||||
|
||||
tradeController.initWithData(orderBookListItem.getOffer(), requestedAmount);
|
||||
takerTradeController.initWithData(offer, requestedAmount);
|
||||
}
|
||||
|
||||
private void updateOfferList()
|
||||
private void removeOffer(Offer offer)
|
||||
{
|
||||
orderBook.updateFilter(orderBookFilter);
|
||||
try
|
||||
{
|
||||
orderBook.removeOffer(offer);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
private void applyOffers()
|
||||
{
|
||||
orderBook.applyFilter(orderBookFilter);
|
||||
|
||||
priceColumn.setSortType((orderBookFilter.getDirection() == Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
|
||||
orderBookTable.sort();
|
||||
|
@ -178,6 +235,34 @@ public class OrderBookController implements Initializable, ChildController
|
|||
createOfferButton.setDefaultButton(orderBookTable.getItems().size() == 0);
|
||||
}
|
||||
|
||||
private void setupPolling()
|
||||
{
|
||||
|
||||
pollingTimer = Utilities.setInterval(1000, (AnimationTimer animationTimer) -> {
|
||||
try
|
||||
{
|
||||
messageFacade.getDirtyFlag(user.getCurrentBankAccount().getCurrency());
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
return null;
|
||||
});
|
||||
messageFacade.getIsDirtyProperty().addListener(new ChangeListener<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue)
|
||||
{
|
||||
//log.info("getIsDirtyProperty changed " + oldValue + "/" + newValue);
|
||||
orderBook.loadOffers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Table columns
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setDirectionColumnCellFactory()
|
||||
{
|
||||
directionColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper(offer.getValue()));
|
||||
|
@ -205,22 +290,35 @@ public class OrderBookController implements Initializable, ChildController
|
|||
{
|
||||
String title;
|
||||
Image icon;
|
||||
if (orderBookListItem.getOffer().getDirection() == Direction.SELL)
|
||||
Offer offer = orderBookListItem.getOffer();
|
||||
|
||||
if (offer.getMessagePubKeyAsHex().equals(user.getMessagePubKeyAsHex()))
|
||||
{
|
||||
icon = buyIcon;
|
||||
title = Formatter.formatDirection(Direction.BUY, true);
|
||||
icon = Icons.getIconImage(Icons.REMOVE);
|
||||
title = "Remove";
|
||||
button.setOnAction(event -> removeOffer(orderBookListItem.getOffer()));
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = sellIcon;
|
||||
title = Formatter.formatDirection(Direction.SELL, true);
|
||||
if (offer.getDirection() == Direction.SELL)
|
||||
{
|
||||
icon = buyIcon;
|
||||
title = Formatter.formatDirection(Direction.BUY, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = sellIcon;
|
||||
title = Formatter.formatDirection(Direction.SELL, true);
|
||||
}
|
||||
|
||||
button.setDefaultButton(getIndex() == 0);
|
||||
button.setOnAction(event -> takeOffer(orderBookListItem.getOffer()));
|
||||
}
|
||||
|
||||
|
||||
iconView.setImage(icon);
|
||||
button.setText(title);
|
||||
setGraphic(button);
|
||||
|
||||
button.setDefaultButton(getIndex() == 0);
|
||||
button.setOnAction(event -> openTradeTab(orderBookListItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -305,6 +403,11 @@ public class OrderBookController implements Initializable, ChildController
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private double textInputToNumber(String oldValue, String newValue)
|
||||
{
|
||||
//TODO use regex.... or custom textfield component
|
||||
|
@ -330,24 +433,5 @@ public class OrderBookController implements Initializable, ChildController
|
|||
double p = textInputToNumber(price.getText(), price.getText());
|
||||
volume.setText(Formatter.formatPrice(a * p));
|
||||
}
|
||||
|
||||
|
||||
// the scrollbar width is not handled correctly from the layout initially
|
||||
/* private void forceTableLayoutUpdate()
|
||||
{
|
||||
final List<OrderBookListItem> items = orderBookTable.getItems();
|
||||
if (items == null || items.size() == 0) return;
|
||||
|
||||
final OrderBookListItem item = orderBookTable.getItems().get(0);
|
||||
items.remove(0);
|
||||
Platform.runLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
items.add(0, item);
|
||||
}
|
||||
});
|
||||
} */
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.bitsquare.gui.market.orderbook;
|
||||
|
||||
import io.bitsquare.btc.BtcFormatter;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
|
@ -10,19 +11,22 @@ import javafx.beans.property.StringProperty;
|
|||
*/
|
||||
public class OrderBookListItem
|
||||
{
|
||||
private final StringProperty price = new SimpleStringProperty();
|
||||
private final StringProperty amount = new SimpleStringProperty();
|
||||
private final StringProperty volume = new SimpleStringProperty();
|
||||
protected final StringProperty price = new SimpleStringProperty();
|
||||
protected final StringProperty amount = new SimpleStringProperty();
|
||||
protected final StringProperty volume = new SimpleStringProperty();
|
||||
|
||||
private Offer offer;
|
||||
protected Offer offer;
|
||||
|
||||
|
||||
public OrderBookListItem(Offer offer)
|
||||
{
|
||||
this.offer = offer;
|
||||
|
||||
double amountAsBtcDouble = BtcFormatter.satoshiToBTC(offer.getAmount());
|
||||
double minAmountAsBtcDouble = BtcFormatter.satoshiToBTC(offer.getMinAmount());
|
||||
|
||||
this.price.set(Formatter.formatPrice(offer.getPrice()));
|
||||
this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount().doubleValue(), offer.getMinAmount().doubleValue()));
|
||||
this.amount.set(Formatter.formatAmountWithMinAmount(amountAsBtcDouble, minAmountAsBtcDouble));
|
||||
this.volume.set(Formatter.formatVolumeWithMinVolume(offer.getVolume(), offer.getMinVolume()));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
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;
|
||||
|
@ -16,12 +13,17 @@ import io.bitsquare.gui.util.Converter;
|
|||
import io.bitsquare.gui.util.FormBuilder;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import io.bitsquare.gui.util.Popups;
|
||||
import io.bitsquare.trade.*;
|
||||
import io.bitsquare.util.Utils;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.Trading;
|
||||
import io.bitsquare.trade.taker.TakerPaymentProtocol;
|
||||
import io.bitsquare.trade.taker.TakerPaymentProtocolListener;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
|
@ -35,17 +37,16 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class TradeController implements Initializable, ChildController, WalletFacade.WalletListener
|
||||
public class TakerTradeController implements Initializable, ChildController, WalletFacade.WalletListener
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(TradeController.class);
|
||||
private static final int SIM_DELAY = 2000;
|
||||
private static final Logger log = LoggerFactory.getLogger(TakerTradeController.class);
|
||||
|
||||
private Trading trading;
|
||||
private WalletFacade walletFacade;
|
||||
private BlockChainFacade blockChainFacade;
|
||||
private MessageFacade messageFacade;
|
||||
private Offer offer;
|
||||
private Trade trade;
|
||||
private Contract contract;
|
||||
private BigInteger requestedAmount;
|
||||
private boolean offererIsOnline;
|
||||
private int row;
|
||||
|
@ -53,10 +54,14 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
private List<ProcessStepItem> processStepItems = new ArrayList();
|
||||
|
||||
private NavigationController navigationController;
|
||||
private TextField amountTextField, totalToPayLabel, totalLabel, collateralTextField;
|
||||
private TextField amountTextField, totalToPayLabel, totalLabel, collateralTextField, isOnlineTextField;
|
||||
private Label statusTextField, infoLabel;
|
||||
private Button nextButton;
|
||||
private ProgressBar progressBar;
|
||||
private AnimationTimer checkOnlineStatusTimer;
|
||||
private Pane isOnlineCheckerHolder;
|
||||
TakerPaymentProtocol takerPaymentProtocol;
|
||||
private Label headerLabel;
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootContainer;
|
||||
|
@ -71,11 +76,12 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public TradeController(Trading trading, WalletFacade walletFacade, BlockChainFacade blockChainFacade)
|
||||
public TakerTradeController(Trading trading, WalletFacade walletFacade, BlockChainFacade blockChainFacade, MessageFacade messageFacade)
|
||||
{
|
||||
this.trading = trading;
|
||||
this.walletFacade = walletFacade;
|
||||
this.blockChainFacade = blockChainFacade;
|
||||
this.messageFacade = messageFacade;
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,9 +94,9 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
this.offer = offer;
|
||||
this.requestedAmount = requestedAmount.compareTo(BigInteger.ZERO) > 0 ? requestedAmount : offer.getAmount();
|
||||
|
||||
trade = trading.createTrade(offer);
|
||||
trade.setTradeAmount(requestedAmount);
|
||||
contract = trading.createContract(trade);
|
||||
// trade = trading.createTrade(offer);
|
||||
//trade.setTradeAmount(requestedAmount);
|
||||
//contract = trading.createContract(trade);
|
||||
|
||||
processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC"));
|
||||
processStepItems.add(new ProcessStepItem("Bank transfer"));
|
||||
|
@ -125,7 +131,13 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
@Override
|
||||
public void cleanup()
|
||||
{
|
||||
if (checkOnlineStatusTimer != null)
|
||||
{
|
||||
checkOnlineStatusTimer.stop();
|
||||
checkOnlineStatusTimer = null;
|
||||
}
|
||||
|
||||
walletFacade.removeRegistrationWalletListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -136,15 +148,20 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
@Override
|
||||
public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks)
|
||||
{
|
||||
log.info("onConfidenceChanged " + numBroadcastPeers + " / " + depthInBlocks);
|
||||
//log.info("onConfidenceChanged " + numBroadcastPeers + " / " + depthInBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsReceived(BigInteger newBalance)
|
||||
{
|
||||
log.info("onCoinsReceived " + newBalance);
|
||||
//log.info("onCoinsReceived " + newBalance);
|
||||
}
|
||||
|
||||
//TODO
|
||||
public void onPingPeerResult(boolean success)
|
||||
{
|
||||
setIsOnlineStatus(success);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
|
@ -173,24 +190,29 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.formatSatoshis(Fees.OFFER_TAKER_FEE, false), ++row);
|
||||
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPay(), ++row);
|
||||
|
||||
isOnlineTextField = FormBuilder.addTextField(gridPane, "Online status:", "Checking offerers online status...", ++row);
|
||||
ProgressIndicator isOnlineChecker = new ProgressIndicator();
|
||||
isOnlineChecker.setPrefSize(20, 20);
|
||||
isOnlineChecker.setLayoutY(3);
|
||||
isOnlineCheckerHolder = new Pane();
|
||||
isOnlineCheckerHolder.getChildren().addAll(isOnlineChecker);
|
||||
gridPane.add(isOnlineCheckerHolder, 2, row);
|
||||
|
||||
messageFacade.pingPeer(offer.getMessagePubKeyAsHex());
|
||||
checkOnlineStatusTimer = Utilities.setTimeout(5000, (AnimationTimer animationTimer) -> {
|
||||
setIsOnlineStatus(false);
|
||||
return null;
|
||||
});
|
||||
|
||||
nextButton = FormBuilder.addButton(gridPane, "Take offer and pay", ++row);
|
||||
nextButton.setDefaultButton(true);
|
||||
nextButton.setOnAction(e -> initTrade());
|
||||
nextButton.setOnAction(e -> takeOffer());
|
||||
|
||||
// details
|
||||
FormBuilder.addVSpacer(gridPane, ++row);
|
||||
FormBuilder.addHeaderLabel(gridPane, "Offerer details:", ++row);
|
||||
TextField isOnlineTextField = FormBuilder.addTextField(gridPane, "Online status:", "Checking offerers online status...", ++row);
|
||||
ProgressIndicator isOnlineChecker = new ProgressIndicator();
|
||||
isOnlineChecker.setPrefSize(20, 20);
|
||||
isOnlineChecker.setLayoutY(3);
|
||||
Pane isOnlineCheckerHolder = new Pane();
|
||||
isOnlineCheckerHolder.getChildren().addAll(isOnlineChecker);
|
||||
gridPane.add(isOnlineCheckerHolder, 2, row);
|
||||
checkIfOffererIsOnline(isOnlineCheckerHolder, isOnlineTextField);
|
||||
|
||||
FormBuilder.addTextField(gridPane, "Bank account type:", offer.getBankAccountTypeEnum().toString(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Bank account country:", offer.getBankAccountCountryLocale().getDisplayCountry(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Country:", offer.getBankAccountCountryLocale().getDisplayCountry(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Arbitrator:", offer.getArbitrator().getName(), ++row);
|
||||
Label arbitratorLink = new Label(offer.getArbitrator().getUrl());
|
||||
arbitratorLink.setId("label-url");
|
||||
|
@ -198,23 +220,18 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
arbitratorLink.setOnMouseClicked(e -> {
|
||||
try
|
||||
{
|
||||
Utils.openURL(offer.getArbitrator().getUrl());
|
||||
Utilities.openURL(offer.getArbitrator().getUrl());
|
||||
} catch (Exception e1)
|
||||
{
|
||||
log.warn(e1.toString());
|
||||
}
|
||||
});
|
||||
|
||||
FormBuilder.addVSpacer(gridPane, ++row);
|
||||
FormBuilder.addHeaderLabel(gridPane, "More details:", ++row);
|
||||
FormBuilder.addTextField(gridPane, "Offer ID:", offer.getUid().toString(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Account ID:", offer.getAccountID(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Messaging ID:", offer.getMessageID(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Supported languages:", Formatter.languageLocalesToString(offer.getAcceptedLanguageLocales()), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Supported countries:", Formatter.countryLocalesToString(offer.getAcceptedCountryLocales()), ++row);
|
||||
}
|
||||
|
||||
private void initTrade()
|
||||
private void takeOffer()
|
||||
{
|
||||
if (!tradeAmountValid())
|
||||
{
|
||||
|
@ -222,7 +239,7 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
return;
|
||||
}
|
||||
|
||||
if (!blockChainFacade.verifyEmbeddedData(offer.getAccountID()))
|
||||
/* if (!blockChainFacade.verifyAccountRegistration(offer.getAccountID()))
|
||||
{
|
||||
Popups.openErrorPopup("Offerers account ID not valid", "Offerers registration tx is not found in blockchain or does not match the requirements.");
|
||||
return;
|
||||
|
@ -232,7 +249,7 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
{
|
||||
Popups.openErrorPopup("Offerers account ID is blacklisted", "Offerers account ID is blacklisted.");
|
||||
return;
|
||||
}
|
||||
} */
|
||||
|
||||
amountTextField.setEditable(false);
|
||||
|
||||
|
@ -258,101 +275,80 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
progressIndicatorHolder.getChildren().addAll(progressIndicator);
|
||||
gridPane.add(progressIndicatorHolder, 1, row);
|
||||
|
||||
trade = trading.createTrade(offer);
|
||||
trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
|
||||
|
||||
trading.sendTakeOfferRequest(trade);
|
||||
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
|
||||
onTakeOfferRequestConfirmed();
|
||||
progressBar.setProgress(1.0 / 3.0);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void onTakeOfferRequestConfirmed()
|
||||
{
|
||||
FutureCallback callback = new FutureCallback<Transaction>()
|
||||
takerPaymentProtocol = trading.addTakerPaymentProtocol(trade, new TakerPaymentProtocolListener()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
public void onProgress(double progress)
|
||||
{
|
||||
log.info("sendResult onSuccess:" + transaction.toString());
|
||||
trade.setTakeOfferFeeTxID(transaction.getHashAsString());
|
||||
progressBar.setProgress(progress);
|
||||
|
||||
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;
|
||||
});
|
||||
/*switch (state)
|
||||
{
|
||||
case FOUND_PEER_ADDRESS:
|
||||
statusTextField.setText("Peer found.");
|
||||
break;
|
||||
case SEND_TAKE_OFFER_REQUEST_ARRIVED:
|
||||
statusTextField.setText("Take offer request successfully sent to peer.");
|
||||
break;
|
||||
case SEND_TAKE_OFFER_REQUEST_ACCEPTED:
|
||||
statusTextField.setText("Take offer request accepted by peer.");
|
||||
break;
|
||||
case SEND_TAKE_OFFER_REQUEST_REJECTED:
|
||||
statusTextField.setText("Take offer request rejected by peer.");
|
||||
break;
|
||||
case INSUFFICIENT_MONEY_FOR_OFFER_FEE:
|
||||
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
|
||||
break;
|
||||
case OFFER_FEE_PAYED:
|
||||
statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
|
||||
break;
|
||||
} */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
public void onFailure(String failureMessage)
|
||||
{
|
||||
log.warn("sendResult onFailure:" + t.toString());
|
||||
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
||||
log.warn(failureMessage);
|
||||
}
|
||||
};
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onDepositTxPublished(String depositTxID)
|
||||
{
|
||||
buildDepositPublishedScreen(depositTxID);
|
||||
}
|
||||
|
||||
private void onOfferFeePaymentConfirmed()
|
||||
{
|
||||
trading.requestOffererDetailData();
|
||||
statusTextField.setText("Request bank account details from offerer.");
|
||||
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
|
||||
onUserDetailsReceived();
|
||||
progressBar.setProgress(1.0);
|
||||
return null;
|
||||
@Override
|
||||
public void onBankTransferInited()
|
||||
{
|
||||
buildBankTransferInitedScreen();
|
||||
}
|
||||
});
|
||||
|
||||
takerPaymentProtocol.takeOffer();
|
||||
}
|
||||
|
||||
private void onUserDetailsReceived()
|
||||
private void buildDepositPublishedScreen(String depositTxID)
|
||||
{
|
||||
if (!blockChainFacade.verifyEmbeddedData(offer.getAccountID()))
|
||||
{
|
||||
Popups.openErrorPopup("Offerers bank account is blacklisted", "Offerers bank account is blacklisted.");
|
||||
return;
|
||||
}
|
||||
|
||||
trading.signContract(contract);
|
||||
trading.payToDepositTx(trade);
|
||||
|
||||
buildWaitBankTransfer();
|
||||
}
|
||||
|
||||
private void buildWaitBankTransfer()
|
||||
{
|
||||
processStepBar.next();
|
||||
|
||||
gridPane.getChildren().clear();
|
||||
|
||||
row = -1;
|
||||
FormBuilder.addHeaderLabel(gridPane, "Bank transfer", ++row, 0);
|
||||
infoLabel = FormBuilder.addLabel(gridPane, "Status:", "Wait for Bank transfer.", ++row);
|
||||
|
||||
Utils.setTimeout(SIM_DELAY, (AnimationTimer animationTimer) -> {
|
||||
onBankTransferInited();
|
||||
return null;
|
||||
});
|
||||
headerLabel = FormBuilder.addHeaderLabel(gridPane, "Deposit transaction published", ++row, 0);
|
||||
infoLabel = FormBuilder.addLabel(gridPane, "Status:", "Deposit transaction published by offerer.\nAs soon as the offerer starts the \nBank transfer, you will get informed.", ++row);
|
||||
FormBuilder.addTextField(gridPane, "Transaction ID:", depositTxID, ++row, false, true);
|
||||
}
|
||||
|
||||
private void onBankTransferInited()
|
||||
private void buildBankTransferInitedScreen()
|
||||
{
|
||||
row = 1;
|
||||
infoLabel.setText("Bank transfer has been inited.");
|
||||
Label label = FormBuilder.addLabel(gridPane, "", "Check your bank account and continue when you have received the money.", ++row);
|
||||
GridPane.setColumnSpan(label, 2);
|
||||
processStepBar.next();
|
||||
|
||||
headerLabel.setText("Bank transfer inited");
|
||||
infoLabel.setText("Check your bank account and continue \nwhen you have received the money.");
|
||||
log.info("#### grid " + gridPane.getChildren().size());
|
||||
gridPane.add(nextButton, 1, ++row);
|
||||
nextButton.setText("I have received the bank transfer");
|
||||
nextButton.setText("I have received the money at my bank");
|
||||
nextButton.setOnAction(e -> releaseBTC());
|
||||
}
|
||||
|
||||
|
@ -405,16 +401,20 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
return offer.getDirection() == Direction.BUY;
|
||||
}
|
||||
|
||||
|
||||
private void checkIfOffererIsOnline(Node isOnlineChecker, TextField isOnlineTextField)
|
||||
private void setIsOnlineStatus(boolean isOnline)
|
||||
{
|
||||
// mock
|
||||
Utils.setTimeout(3000, (AnimationTimer animationTimer) -> {
|
||||
offererIsOnline = Math.random() > 0.3 ? true : false;
|
||||
isOnlineTextField.setText(offererIsOnline ? "Online" : "Offline");
|
||||
gridPane.getChildren().remove(isOnlineChecker);
|
||||
return null;
|
||||
});
|
||||
if (checkOnlineStatusTimer != null)
|
||||
{
|
||||
checkOnlineStatusTimer.stop();
|
||||
checkOnlineStatusTimer = null;
|
||||
}
|
||||
|
||||
offererIsOnline = isOnline;
|
||||
isOnlineTextField.setText(offererIsOnline ? "Online" : "Offline");
|
||||
gridPane.getChildren().remove(isOnlineCheckerHolder);
|
||||
|
||||
isOnlineTextField.setId(isOnline ? "online-label" : "offline-label");
|
||||
isOnlineTextField.layout();
|
||||
}
|
||||
|
||||
private void applyVolume()
|
||||
|
@ -448,7 +448,10 @@ public class TradeController implements Initializable, ChildController, WalletFa
|
|||
|
||||
private BigInteger getCollateralInSatoshis()
|
||||
{
|
||||
return BtcFormatter.doubleValueToSatoshis(Converter.stringToDouble(amountTextField.getText()) * offer.getCollateral());
|
||||
double amount = Converter.stringToDouble(amountTextField.getText());
|
||||
double resultDouble = amount * (double) offer.getCollateral() / 100.0;
|
||||
BigInteger result = BtcFormatter.doubleValueToSatoshis(resultDouble);
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigInteger getAmountInSatoshis()
|
|
@ -5,13 +5,13 @@
|
|||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="rootContainer" fx:controller="io.bitsquare.gui.market.trade.TradeController"
|
||||
<AnchorPane fx:id="rootContainer" fx:controller="io.bitsquare.gui.market.trade.TakerTradeController"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<ScrollPane fitToWidth="true" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0"
|
||||
AnchorPane.bottomAnchor="0">
|
||||
|
||||
<content>
|
||||
<VBox fx:id="vBox" spacing="10">
|
||||
<VBox spacing="10">
|
||||
<padding>
|
||||
<Insets left="10" right="10" top="10" bottom="10"/>
|
||||
</padding>
|
|
@ -1,11 +1,12 @@
|
|||
package io.bitsquare.gui.msg;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.BitSquare;
|
||||
import io.bitsquare.gui.ChildController;
|
||||
import io.bitsquare.gui.NavigationController;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.MessageListener;
|
||||
import io.bitsquare.msg.listeners.OrderBookListener;
|
||||
import io.bitsquare.msg.listeners.PingPeerListener;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -26,7 +27,7 @@ import java.util.Arrays;
|
|||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class MsgController implements Initializable, ChildController, MessageListener
|
||||
public class MsgController implements Initializable, ChildController, OrderBookListener, PingPeerListener
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MsgController.class);
|
||||
|
||||
|
@ -69,8 +70,8 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
myID = WalletFacade.WALLET_PREFIX;
|
||||
otherID = WalletFacade.WALLET_PREFIX.equals("taker") ? "offerer" : "taker";
|
||||
myID = BitSquare.ID;
|
||||
otherID = BitSquare.ID.equals("taker") ? "offerer" : "taker";
|
||||
|
||||
messageFacade.addMessageListener(this);
|
||||
|
||||
|
@ -94,14 +95,14 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
// Interface implementation: MessageListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
/* @Override
|
||||
public void onMessage(Object message)
|
||||
{
|
||||
sendButton.setDisable(!messageFacade.isOtherPeerDefined());
|
||||
|
||||
if (message instanceof String)
|
||||
chatTextArea.appendText("\n" + otherID + ": " + message);
|
||||
}
|
||||
} */
|
||||
|
||||
@Override
|
||||
public void onPing()
|
||||
|
@ -110,12 +111,12 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onOfferPublished(boolean success)
|
||||
public void onOfferAdded(Data offerData, boolean success)
|
||||
{
|
||||
if (success)
|
||||
getOffers();
|
||||
else
|
||||
log.warn("onOfferPublished returned false");
|
||||
log.warn("onOfferAdded returned false");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,7 +146,7 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
|
||||
|
||||
@Override
|
||||
public void onOfferRemoved(boolean success)
|
||||
public void onOfferRemoved(Data offerData, boolean success)
|
||||
{
|
||||
if (success)
|
||||
getOffers();
|
||||
|
@ -153,7 +154,7 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
log.warn("onOfferRemoved failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
/* @Override
|
||||
public void onResponseFromSend(Object response)
|
||||
{
|
||||
String msg = (response instanceof String) ? (String) response : null;
|
||||
|
@ -168,14 +169,14 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
public void onSendFailed()
|
||||
{
|
||||
offerTable.getSelectionModel().clearSelection();
|
||||
}
|
||||
} */
|
||||
|
||||
@Override
|
||||
public void onPeerFound()
|
||||
public void onPingPeerResult(boolean success)
|
||||
{
|
||||
sendButton.setDisable(!messageFacade.isOtherPeerDefined());
|
||||
/* sendButton.setDisable(!messageFacade.isOtherPeerDefined());
|
||||
if (pingPending)
|
||||
sendChatMsg(MessageFacade.PING);
|
||||
sendChatMsg(MessageFacade.PING); */
|
||||
}
|
||||
|
||||
|
||||
|
@ -201,14 +202,14 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
@FXML
|
||||
public void publishOffer(ActionEvent actionEvent)
|
||||
{
|
||||
OfferListItem offerListItem = new OfferListItem(offerDataTextField.getText(), messageFacade.getPubKeyAsHex(), currencyTextField.getText());
|
||||
/* OfferListItem offerListItem = new OfferListItem(offerDataTextField.getText(), messageFacade.getPubKeyAsHex(), currencyTextField.getText());
|
||||
try
|
||||
{
|
||||
messageFacade.publishOffer(currencyTextField.getText(), offerListItem);
|
||||
messageFacade.addOffer(currencyTextField.getText(), offerListItem);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -232,14 +233,14 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
private void inviteForChat(OfferListItem item, int index)
|
||||
{
|
||||
selectedIndex = index;
|
||||
messageFacade.findPeer(item.getPubKey());
|
||||
// messageFacade.pingPeer(item.getPubKey());
|
||||
pingPending = true;
|
||||
|
||||
}
|
||||
|
||||
private void sendChatMsg(String msg)
|
||||
{
|
||||
messageFacade.sendMessage(msg);
|
||||
// messageFacade.sendMessage(msg);
|
||||
|
||||
chatTextArea.appendText("\n" + myID + ": " + msg);
|
||||
chatInputField.setText("");
|
||||
|
@ -252,13 +253,13 @@ public class MsgController implements Initializable, ChildController, MessageLis
|
|||
|
||||
private void removeOffer(OfferListItem offer)
|
||||
{
|
||||
try
|
||||
/* try
|
||||
{
|
||||
messageFacade.removeOffer(currencyTextField.getText(), offer);
|
||||
messageFacade.removeOffer(offer);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,29 +1,111 @@
|
|||
package io.bitsquare.gui.orders;
|
||||
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.inject.Inject;
|
||||
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.gui.ChildController;
|
||||
import io.bitsquare.gui.NavigationController;
|
||||
import io.bitsquare.gui.util.Icons;
|
||||
import io.bitsquare.gui.util.Localisation;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.Trading;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.util.Callback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.*;
|
||||
|
||||
public class OrdersController implements Initializable, ChildController
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(OrdersController.class);
|
||||
|
||||
private Trading trading;
|
||||
private Trade currentTrade;
|
||||
|
||||
private Image buyIcon = Icons.getIconImage(Icons.BUY);
|
||||
private Image sellIcon = Icons.getIconImage(Icons.SELL);
|
||||
|
||||
@FXML
|
||||
private TableView openTradesTable;
|
||||
@FXML
|
||||
private TableColumn<String, TradesTableItem> directionColumn, countryColumn, bankAccountTypeColumn, priceColumn, amountColumn, volumeColumn, statusColumn, selectColumn;
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
@FXML
|
||||
private Label confidenceLabel, txIDCopyIcon, holderNameCopyIcon, primaryBankAccountIDCopyIcon, secondaryBankAccountIDCopyIcon;
|
||||
@FXML
|
||||
private TextField txIDTextField, bankAccountTypeTextField, holderNameTextField, primaryBankAccountIDTextField, secondaryBankAccountIDTextField;
|
||||
@FXML
|
||||
private Button bankTransferInitedButton;
|
||||
|
||||
|
||||
private NavigationController navigationController;
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OrdersController(Trading trading)
|
||||
{
|
||||
this.trading = trading;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
Map<String, Trade> trades = trading.getTrades();
|
||||
List<Trade> tradeList = new ArrayList<>(trades.values());
|
||||
ObservableList<TradesTableItem> tradeItems = FXCollections.observableArrayList();
|
||||
for (Iterator<Trade> iterator = tradeList.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
Trade trade = iterator.next();
|
||||
tradeItems.add(new TradesTableItem(trade));
|
||||
}
|
||||
|
||||
setCountryColumnCellFactory();
|
||||
setBankAccountTypeColumnCellFactory();
|
||||
setDirectionColumnCellFactory();
|
||||
setSelectColumnCellFactory();
|
||||
|
||||
openTradesTable.setItems(tradeItems);
|
||||
openTradesTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
|
||||
openTradesTable.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
|
||||
if (newValue instanceof TradesTableItem)
|
||||
{
|
||||
TradesTableItem tradesTableItem = (TradesTableItem) newValue;
|
||||
fillData(tradesTableItem.getTrade());
|
||||
}
|
||||
});
|
||||
|
||||
initCopyIcons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigationController(NavigationController navigationController)
|
||||
{
|
||||
this.navigationController = navigationController;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,5 +114,284 @@ public class OrdersController implements Initializable, ChildController
|
|||
|
||||
}
|
||||
|
||||
public void bankTransferInited(ActionEvent actionEvent)
|
||||
{
|
||||
trading.onBankTransferInited(currentTrade.getUid());
|
||||
}
|
||||
|
||||
private void updateTx(Trade trade)
|
||||
{
|
||||
Transaction transaction = trade.getDepositTransaction();
|
||||
String txID = "";
|
||||
if (transaction != null)
|
||||
{
|
||||
txID = transaction.getHashAsString();
|
||||
|
||||
transaction.getConfidence().addEventListener(new TransactionConfidence.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onConfidenceChanged(Transaction tx, ChangeReason reason)
|
||||
{
|
||||
updateConfidence(tx);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
updateConfidence(transaction);
|
||||
}
|
||||
txIDTextField.setText(txID);
|
||||
}
|
||||
|
||||
private void updateConfidence(Transaction tx)
|
||||
{
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
|
||||
switch (confidence.getConfidenceType())
|
||||
{
|
||||
case UNKNOWN:
|
||||
confidenceLabel.setText("");
|
||||
progressIndicator.setProgress(0);
|
||||
break;
|
||||
case PENDING:
|
||||
confidenceLabel.setText("Seen by " + confidence.numBroadcastPeers() + " peer(s)");
|
||||
progressIndicator.setProgress(-1);
|
||||
break;
|
||||
case BUILDING:
|
||||
bankTransferInitedButton.setOpacity(1);
|
||||
confidenceLabel.setText("Confirmed in " + confidence.getDepthInBlocks() + " block(s)");
|
||||
progressIndicator.setProgress(Math.min(1, (double) confidence.getDepthInBlocks() / 6.0));
|
||||
break;
|
||||
case DEAD:
|
||||
confidenceLabel.setText("Transaction is invalid.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void fillData(Trade trade)
|
||||
{
|
||||
currentTrade = trade;
|
||||
Transaction transaction = trade.getDepositTransaction();
|
||||
if (transaction == null)
|
||||
{
|
||||
trade.getDepositTxChangedProperty().addListener(new ChangeListener<Boolean>()
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean aBoolean2)
|
||||
{
|
||||
updateTx(trade);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
updateTx(trade);
|
||||
}
|
||||
|
||||
// back details
|
||||
BankAccount bankAccount = trade.getContract().getTakerBankAccount();
|
||||
bankAccountTypeTextField.setText(bankAccount.getBankAccountType().getType().toString());
|
||||
holderNameTextField.setText(bankAccount.getAccountHolderName());
|
||||
primaryBankAccountIDTextField.setText(bankAccount.getAccountPrimaryID());
|
||||
secondaryBankAccountIDTextField.setText(bankAccount.getAccountSecondaryID());
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Table columns
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setCountryColumnCellFactory()
|
||||
{
|
||||
countryColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper(offer.getValue()));
|
||||
countryColumn.setCellFactory(new Callback<TableColumn<String, TradesTableItem>, TableCell<String, TradesTableItem>>()
|
||||
{
|
||||
@Override
|
||||
public TableCell<String, TradesTableItem> call(TableColumn<String, TradesTableItem> directionColumn)
|
||||
{
|
||||
return new TableCell<String, TradesTableItem>()
|
||||
{
|
||||
final HBox hBox = new HBox();
|
||||
|
||||
{
|
||||
hBox.setSpacing(3);
|
||||
hBox.setAlignment(Pos.CENTER);
|
||||
setGraphic(hBox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final TradesTableItem orderBookListItem, boolean empty)
|
||||
{
|
||||
super.updateItem(orderBookListItem, empty);
|
||||
|
||||
hBox.getChildren().clear();
|
||||
if (orderBookListItem != null)
|
||||
{
|
||||
Locale countryLocale = orderBookListItem.getTrade().getOffer().getBankAccountCountryLocale();
|
||||
try
|
||||
{
|
||||
hBox.getChildren().add(Icons.getIconImageView("/images/countries/" + countryLocale.getCountry().toLowerCase() + ".png"));
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.warn("Country icon not found: " + "/images/countries/" + countryLocale.getCountry().toLowerCase() + ".png country name: " + countryLocale.getDisplayCountry());
|
||||
}
|
||||
Tooltip.install(this, new Tooltip(countryLocale.getDisplayCountry()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setBankAccountTypeColumnCellFactory()
|
||||
{
|
||||
bankAccountTypeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper(offer.getValue()));
|
||||
bankAccountTypeColumn.setCellFactory(new Callback<TableColumn<String, TradesTableItem>, TableCell<String, TradesTableItem>>()
|
||||
{
|
||||
@Override
|
||||
public TableCell<String, TradesTableItem> call(TableColumn<String, TradesTableItem> directionColumn)
|
||||
{
|
||||
return new TableCell<String, TradesTableItem>()
|
||||
{
|
||||
@Override
|
||||
public void updateItem(final TradesTableItem orderBookListItem, boolean empty)
|
||||
{
|
||||
super.updateItem(orderBookListItem, empty);
|
||||
|
||||
if (orderBookListItem != null)
|
||||
{
|
||||
BankAccountType.BankAccountTypeEnum bankAccountTypeEnum = orderBookListItem.getTrade().getOffer().getBankAccountTypeEnum();
|
||||
setText(Localisation.get(bankAccountTypeEnum.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setDirectionColumnCellFactory()
|
||||
{
|
||||
directionColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper(offer.getValue()));
|
||||
directionColumn.setCellFactory(new Callback<TableColumn<String, TradesTableItem>, TableCell<String, TradesTableItem>>()
|
||||
{
|
||||
@Override
|
||||
public TableCell<String, TradesTableItem> call(TableColumn<String, TradesTableItem> directionColumn)
|
||||
{
|
||||
return new TableCell<String, TradesTableItem>()
|
||||
{
|
||||
final ImageView iconView = new ImageView();
|
||||
final Button button = new Button();
|
||||
|
||||
{
|
||||
button.setGraphic(iconView);
|
||||
button.setMinWidth(70);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(final TradesTableItem orderBookListItem, boolean empty)
|
||||
{
|
||||
super.updateItem(orderBookListItem, empty);
|
||||
|
||||
if (orderBookListItem != null)
|
||||
{
|
||||
String title;
|
||||
Image icon;
|
||||
Offer offer = orderBookListItem.getTrade().getOffer();
|
||||
|
||||
if (offer.getDirection() == Direction.SELL)
|
||||
{
|
||||
icon = buyIcon;
|
||||
title = io.bitsquare.gui.util.Formatter.formatDirection(Direction.BUY, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
icon = sellIcon;
|
||||
title = io.bitsquare.gui.util.Formatter.formatDirection(Direction.SELL, true);
|
||||
}
|
||||
button.setDisable(true);
|
||||
iconView.setImage(icon);
|
||||
button.setText(title);
|
||||
setGraphic(button);
|
||||
}
|
||||
else
|
||||
{
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setSelectColumnCellFactory()
|
||||
{
|
||||
selectColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper(offer.getValue()));
|
||||
selectColumn.setCellFactory(new Callback<TableColumn<String, TradesTableItem>, TableCell<String, TradesTableItem>>()
|
||||
{
|
||||
@Override
|
||||
public TableCell<String, TradesTableItem> call(TableColumn<String, TradesTableItem> directionColumn)
|
||||
{
|
||||
return new TableCell<String, TradesTableItem>()
|
||||
{
|
||||
final Button button = new Button("Select");
|
||||
|
||||
@Override
|
||||
public void updateItem(final TradesTableItem orderBookListItem, boolean empty)
|
||||
{
|
||||
super.updateItem(orderBookListItem, empty);
|
||||
|
||||
if (orderBookListItem != null)
|
||||
{
|
||||
setGraphic(button);
|
||||
}
|
||||
else
|
||||
{
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void initCopyIcons()
|
||||
{
|
||||
AwesomeDude.setIcon(txIDCopyIcon, AwesomeIcon.COPY);
|
||||
txIDCopyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(txIDTextField.getText());
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
|
||||
AwesomeDude.setIcon(holderNameCopyIcon, AwesomeIcon.COPY);
|
||||
holderNameCopyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(holderNameCopyIcon.getText());
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
|
||||
AwesomeDude.setIcon(primaryBankAccountIDCopyIcon, AwesomeIcon.COPY);
|
||||
primaryBankAccountIDCopyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(primaryBankAccountIDCopyIcon.getText());
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
|
||||
AwesomeDude.setIcon(secondaryBankAccountIDCopyIcon, AwesomeIcon.COPY);
|
||||
secondaryBankAccountIDCopyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(secondaryBankAccountIDCopyIcon.getText());
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,148 @@
|
|||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<Pane fx:controller="io.bitsquare.gui.orders.OrdersController"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Label text="Orders"/>
|
||||
</Pane>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<VBox prefHeight="540" prefWidth="800" spacing="10" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0"
|
||||
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="io.bitsquare.gui.orders.OrdersController">
|
||||
<children>
|
||||
<Label id="headline-label" text="Open trades">
|
||||
<VBox.margin>
|
||||
<Insets left="10.0" top="10.0"/>
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
|
||||
<TableView fx:id="openTradesTable" id="orderbook-table" prefHeight="150.0">
|
||||
<columns>
|
||||
<TableColumn fx:id="amountColumn" minWidth="120" text="Amount (Min.)">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="amount"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="priceColumn" minWidth="70" text="Price">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="price"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="volumeColumn" minWidth="130" text="Volume (Min.)">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="volume"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="countryColumn" minWidth="60" text="Country"/>
|
||||
<TableColumn fx:id="bankAccountTypeColumn" minWidth="140" text="Bank transfer type"/>
|
||||
<TableColumn fx:id="directionColumn" minWidth="80" sortable="false" text="Offer type"/>
|
||||
<TableColumn fx:id="statusColumn" minWidth="80" text="Status">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="status"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
|
||||
<TableColumn fx:id="selectColumn" minWidth="60" sortable="false" text=""/>
|
||||
</columns>
|
||||
<VBox.margin>
|
||||
<Insets left="10.0" right="10.0"/>
|
||||
</VBox.margin>
|
||||
</TableView>
|
||||
|
||||
|
||||
<Label text="After you received 1 blockchain confirmation you are safe to start the bank transfer.">
|
||||
<VBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" top="10.0"/>
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0">
|
||||
<children>
|
||||
<!-- row 0 -->
|
||||
<Label id="headline-label" text="Deposit transaction" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="0"/>
|
||||
|
||||
<!-- row 1 -->
|
||||
<Label text="Transaction ID:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="txIDTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<Label id="copy-icon" fx:id="txIDCopyIcon" minWidth="10" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
<ProgressIndicator fx:id="progressIndicator" prefHeight="20" prefWidth="20" GridPane.columnIndex="3" GridPane.rowIndex="1"/>
|
||||
<Label fx:id="confidenceLabel" text="Checking confirmations..." GridPane.columnIndex="4" GridPane.rowIndex="1"/>
|
||||
|
||||
<!-- row 2 -->
|
||||
<Label id="headline-label" text="Bank details" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="2"/>
|
||||
|
||||
<!-- row 3 -->
|
||||
<Label text="Bank account type:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
<!-- row 4 -->
|
||||
<Label text="Holder name:" GridPane.columnIndex="0" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="holderNameTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
||||
<Label id="copy-icon" fx:id="holderNameCopyIcon" minWidth="10" GridPane.columnIndex="2" GridPane.rowIndex="4">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<!-- row 5 -->
|
||||
<Label text="Primary bank account ID:" GridPane.columnIndex="0" GridPane.rowIndex="5"/>
|
||||
<TextField fx:id="primaryBankAccountIDTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
|
||||
<Label id="copy-icon" fx:id="primaryBankAccountIDCopyIcon" minWidth="10" GridPane.columnIndex="2" GridPane.rowIndex="5">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<!-- row 6 -->
|
||||
<Label text="Secondary bank account ID:" GridPane.columnIndex="0" GridPane.rowIndex="6"/>
|
||||
<TextField fx:id="secondaryBankAccountIDTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="6"/>
|
||||
<Label id="copy-icon" fx:id="secondaryBankAccountIDCopyIcon" minWidth="10" GridPane.columnIndex="2" GridPane.rowIndex="6">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<!-- row 7 -->
|
||||
<Button fx:id="bankTransferInitedButton" defaultButton="true" opacity="0.5" onAction="#bankTransferInited" text="Bank transfer inited"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||
|
||||
</children>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS" minWidth="400"/>
|
||||
<ColumnConstraints fillWidth="false" halignment="LEFT" hgrow="NEVER"/>
|
||||
<ColumnConstraints fillWidth="false" halignment="LEFT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints fillWidth="false" halignment="LEFT" hgrow="SOMETIMES"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES"/>
|
||||
<RowConstraints/>
|
||||
</rowConstraints>
|
||||
<VBox.margin>
|
||||
<Insets left="10.0" right="10.0"/>
|
||||
</VBox.margin>
|
||||
</GridPane>
|
||||
</children>
|
||||
</VBox>
|
||||
|
|
26
src/main/java/io/bitsquare/gui/orders/TradesTableItem.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package io.bitsquare.gui.orders;
|
||||
|
||||
import io.bitsquare.gui.market.orderbook.OrderBookListItem;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TradesTableItem extends OrderBookListItem
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(TradesTableItem.class);
|
||||
|
||||
private Trade trade;
|
||||
|
||||
public TradesTableItem(Trade trade)
|
||||
{
|
||||
super(trade.getOffer());
|
||||
|
||||
this.trade = trade;
|
||||
}
|
||||
|
||||
public Trade getTrade()
|
||||
{
|
||||
return trade;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,14 @@
|
|||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<Pane fx:controller="io.bitsquare.gui.settings.SettingsController"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<Label text="Settings"/>
|
||||
</Pane>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:controller="io.bitsquare.gui.settings.SettingsController" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label id="headline-label" text="Settings"/>
|
||||
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
|
@ -1,35 +1,32 @@
|
|||
package io.bitsquare.gui.setup;
|
||||
|
||||
import com.google.bitcoin.core.InsufficientMoneyException;
|
||||
import com.google.bitcoin.core.*;
|
||||
import com.google.bitcoin.script.Script;
|
||||
import com.google.inject.Inject;
|
||||
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;
|
||||
import io.bitsquare.gui.components.ConfirmationComponent;
|
||||
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.Localisation;
|
||||
import io.bitsquare.gui.util.Popups;
|
||||
import io.bitsquare.gui.util.Verification;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utils;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.Clipboard;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.StringConverter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -38,34 +35,36 @@ import java.math.BigInteger;
|
|||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
public class SetupController implements Initializable, ChildController, WalletFacade.WalletListener
|
||||
public class SetupController implements Initializable, ChildController
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(SetupController.class);
|
||||
|
||||
private final User user;
|
||||
private final WalletFacade walletFacade;
|
||||
private NavigationController navigationController;
|
||||
private MessageFacade messageFacade;
|
||||
private final Storage storage;
|
||||
private final List<ProcessStepItem> processStepItems = new ArrayList<>();
|
||||
private NavigationController navigationController;
|
||||
private TextField balanceLabel, accountTitle, accountHolderName, accountPrimaryID, accountSecondaryID;
|
||||
private ComboBox<Locale> countryComboBox;
|
||||
private ComboBox<BankAccountType> bankTransferTypeComboBox;
|
||||
private ComboBox<Currency> currencyComboBox;
|
||||
private Button addBankAccountButton;
|
||||
|
||||
@FXML
|
||||
private AnchorPane rootContainer;
|
||||
@FXML
|
||||
private Label infoLabel;
|
||||
private TitledPane payRegistrationFeePane, addBankAccountPane, settingsPane;
|
||||
@FXML
|
||||
private ProcessStepBar<String> processStepBar;
|
||||
private Label payRegFeeInfoLabel, addBankAccountInfoLabel, copyIcon, confirmationLabel;
|
||||
@FXML
|
||||
private GridPane gridPane;
|
||||
private TextField registrationAddressTextField, balanceTextField, accountTitle, accountHolderName, accountPrimaryID, accountSecondaryID;
|
||||
@FXML
|
||||
private Button nextButton, skipButton;
|
||||
private Button createAccountButton, addBankAccountButton;
|
||||
@FXML
|
||||
private VBox vBox;
|
||||
private Accordion accordion;
|
||||
@FXML
|
||||
private ComboBox<Locale> countryComboBox;
|
||||
@FXML
|
||||
private ComboBox<BankAccountType> bankAccountTypesComboBox;
|
||||
@FXML
|
||||
private ComboBox<Currency> currencyComboBox;
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -100,17 +99,13 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb)
|
||||
{
|
||||
processStepItems.add(new ProcessStepItem("Fund registration fee"));
|
||||
processStepItems.add(new ProcessStepItem("Add Bank account"));
|
||||
processStepItems.add(new ProcessStepItem("Complete"));
|
||||
processStepBar.setProcessStepItems(processStepItems);
|
||||
setupRegistrationScreen();
|
||||
setupBankAccountScreen();
|
||||
setupSettingsScreen();
|
||||
|
||||
walletFacade.addRegistrationWalletListener(this);
|
||||
|
||||
buildStep0();
|
||||
accordion.setExpandedPane(payRegistrationFeePane);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: ChildController
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -128,84 +123,146 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: WalletFacade.WalletListener
|
||||
// Button handlers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks)
|
||||
public void onPaymentDone(ActionEvent actionEvent)
|
||||
{
|
||||
updateCreateAccountButton();
|
||||
|
||||
log.info("onConfidenceChanged " + numBroadcastPeers + " / " + depthInBlocks);
|
||||
accordion.setExpandedPane(addBankAccountPane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsReceived(BigInteger newBalance)
|
||||
public void onSkipPayment(ActionEvent actionEvent)
|
||||
{
|
||||
updateCreateAccountButton();
|
||||
balanceLabel.setText(BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true));
|
||||
log.info("onCoinsReceived " + newBalance);
|
||||
accordion.setExpandedPane(addBankAccountPane);
|
||||
}
|
||||
|
||||
public void onAddBankAccount(ActionEvent actionEvent)
|
||||
{
|
||||
addBankAccount();
|
||||
storage.write(user.getClass().getName(), user);
|
||||
|
||||
if (verifyBankAccountData())
|
||||
{
|
||||
bankAccountTypesComboBox.getSelectionModel().clearSelection();
|
||||
accountPrimaryID.setText("");
|
||||
accountPrimaryID.setPromptText("");
|
||||
accountSecondaryID.setText("");
|
||||
accountSecondaryID.setPromptText("");
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreateAccount(ActionEvent actionEvent)
|
||||
{
|
||||
addBankAccount();
|
||||
if (user.getBankAccounts().size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
walletFacade.sendRegistrationTx(user.getStringifiedBankAccounts());
|
||||
user.setAccountID(walletFacade.getAccountRegistrationAddress().toString());
|
||||
user.setMessagePubKeyAsHex(DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey()));
|
||||
|
||||
storage.write(user.getClass().getName(), user);
|
||||
|
||||
accordion.setExpandedPane(settingsPane);
|
||||
} catch (InsufficientMoneyException e1)
|
||||
{
|
||||
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onSkipBankAccountSetup(ActionEvent actionEvent)
|
||||
{
|
||||
accordion.setExpandedPane(settingsPane);
|
||||
}
|
||||
|
||||
public void onClose(ActionEvent actionEvent)
|
||||
{
|
||||
navigationController.navigateToView(NavigationController.MARKET, "");
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
// Screens setup
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void buildStep0()
|
||||
private void setupRegistrationScreen()
|
||||
{
|
||||
infoLabel.setText("You need to pay 0.01 BTC to the registration address.\n\n" +
|
||||
payRegFeeInfoLabel.setText("You need to pay 0.01 BTC to the registration address.\n\n" +
|
||||
"That payment will be used to create a unique account connected with your bank account number.\n" +
|
||||
"The privacy of your bank account number will be protected and only revealed to your trading partners.\n" +
|
||||
"The payment will be spent to miners and is needed to store data into the blockchain.\n" +
|
||||
"Your trading account will be the source for your reputation in the trading platform.\n\n" +
|
||||
"You need at least 1 confirmation for doing the registration payment.");
|
||||
|
||||
int row = -1;
|
||||
String registrationAddress = walletFacade.getAccountRegistrationAddress().toString();
|
||||
registrationAddressTextField.setText(registrationAddress);
|
||||
|
||||
TextField addressLabel = FormBuilder.addTextField(gridPane, "Registration address:", walletFacade.getAccountRegistrationAddress().toString(), ++row, false, true);
|
||||
|
||||
Label copyIcon = new Label("");
|
||||
gridPane.add(copyIcon, 2, row);
|
||||
copyIcon.setId("copy-icon");
|
||||
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
|
||||
Tooltip.install(copyIcon, new Tooltip("Copy address to clipboard"));
|
||||
|
||||
balanceLabel = FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
|
||||
|
||||
new ConfirmationComponent(walletFacade, gridPane, ++row);
|
||||
|
||||
nextButton.setText("Payment done");
|
||||
updateCreateAccountButton();
|
||||
|
||||
skipButton.setText("Register later");
|
||||
|
||||
// handlers
|
||||
copyIcon.setOnMouseClicked(e -> {
|
||||
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||
ClipboardContent content = new ClipboardContent();
|
||||
content.putString(addressLabel.getText());
|
||||
content.putString(registrationAddress);
|
||||
clipboard.setContent(content);
|
||||
});
|
||||
|
||||
nextButton.setOnAction(e -> {
|
||||
processStepBar.next();
|
||||
buildStep1();
|
||||
});
|
||||
updateBalance(walletFacade.getAccountRegistrationBalance());
|
||||
|
||||
skipButton.setOnAction(e -> close());
|
||||
walletFacade.getAccountRegistrationWallet().addEventListener(new WalletEventListener()
|
||||
{
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
updateBalance(newBalance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
|
||||
{
|
||||
updateConfidence(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(Wallet wallet, List<ECKey> keys)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsAdded(Wallet wallet, List<Script> scripts)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void buildStep1()
|
||||
private void setupBankAccountScreen()
|
||||
{
|
||||
infoLabel.setText("Add at least one Bank account to your trading account.\n" +
|
||||
addBankAccountInfoLabel.setText("Add at least one Bank account to your trading account.\n" +
|
||||
"That data will be stored in the blockchain in a way that your privacy is protected.\n" +
|
||||
"Only your trading partners will be able to read those data, so your privacy will be protected.");
|
||||
|
||||
gridPane.getChildren().clear();
|
||||
int row = -1;
|
||||
bankTransferTypeComboBox = FormBuilder.addBankAccountComboBox(gridPane, "Bank account type:", Utils.getAllBankAccountTypes(), ++row);
|
||||
bankTransferTypeComboBox.setConverter(new StringConverter<BankAccountType>()
|
||||
bankAccountTypesComboBox.setItems(FXCollections.observableArrayList(Utilities.getAllBankAccountTypes()));
|
||||
currencyComboBox.setItems(FXCollections.observableArrayList(Utilities.getAllCurrencies()));
|
||||
countryComboBox.setItems(FXCollections.observableArrayList(Utilities.getAllLocales()));
|
||||
|
||||
bankAccountTypesComboBox.setConverter(new StringConverter<BankAccountType>()
|
||||
{
|
||||
@Override
|
||||
public String toString(BankAccountType bankAccountType)
|
||||
|
@ -220,14 +277,6 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
}
|
||||
});
|
||||
|
||||
bankTransferTypeComboBox.setPromptText("Select bank account type");
|
||||
accountTitle = FormBuilder.addInputField(gridPane, "Bank account title:", "", ++row);
|
||||
accountHolderName = FormBuilder.addInputField(gridPane, "Bank account holder name:", "", ++row);
|
||||
accountPrimaryID = FormBuilder.addInputField(gridPane, "Bank account primary ID", "", ++row);
|
||||
accountSecondaryID = FormBuilder.addInputField(gridPane, "Bank account secondary ID:", "", ++row);
|
||||
|
||||
currencyComboBox = FormBuilder.addCurrencyComboBox(gridPane, "Currency used for bank account:", Utils.getAllCurrencies(), ++row);
|
||||
currencyComboBox.setPromptText("Select currency");
|
||||
currencyComboBox.setConverter(new StringConverter<Currency>()
|
||||
{
|
||||
@Override
|
||||
|
@ -243,8 +292,6 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
}
|
||||
});
|
||||
|
||||
countryComboBox = FormBuilder.addLocalesComboBox(gridPane, "Country of bank account:", Utils.getAllLocales(), ++row);
|
||||
countryComboBox.setPromptText("Select country");
|
||||
countryComboBox.setConverter(new StringConverter<Locale>()
|
||||
{
|
||||
@Override
|
||||
|
@ -260,21 +307,7 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
addBankAccountButton = new Button("Add other Bank account");
|
||||
gridPane.add(addBankAccountButton, 1, ++row);
|
||||
|
||||
nextButton.setText("Create account");
|
||||
checkCreateAccountButtonState();
|
||||
skipButton.setText("Register later");
|
||||
|
||||
// handlers
|
||||
accountTitle.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountHolderName.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountPrimaryID.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountSecondaryID.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
|
||||
bankTransferTypeComboBox.valueProperty().addListener((ov, oldValue, newValue) -> {
|
||||
bankAccountTypesComboBox.valueProperty().addListener((ov, oldValue, newValue) -> {
|
||||
if (newValue != null && newValue instanceof BankAccountType)
|
||||
{
|
||||
accountPrimaryID.setText("");
|
||||
|
@ -289,132 +322,94 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
currencyComboBox.valueProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
countryComboBox.valueProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
|
||||
addBankAccountButton.setOnAction(e -> {
|
||||
addBankAccount();
|
||||
storage.write(user.getClass().getName(), user);
|
||||
|
||||
if (verifyBankAccountData())
|
||||
{
|
||||
bankTransferTypeComboBox.getSelectionModel().clearSelection();
|
||||
accountPrimaryID.setText("");
|
||||
accountPrimaryID.setPromptText("");
|
||||
accountSecondaryID.setText("");
|
||||
accountSecondaryID.setPromptText("");
|
||||
}
|
||||
});
|
||||
checkCreateAccountButtonState();
|
||||
// handlers
|
||||
accountTitle.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountHolderName.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountPrimaryID.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
accountSecondaryID.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
|
||||
|
||||
nextButton.setOnAction(e -> {
|
||||
addBankAccount();
|
||||
|
||||
if (user.getBankAccounts().size() > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
walletFacade.sendRegistrationTx(user.getStringifiedBankAccounts());
|
||||
user.setAccountID(walletFacade.getAccountRegistrationAddress().toString());
|
||||
user.setMessageID(walletFacade.getAccountRegistrationPubKey());
|
||||
//user.setMessageID(messageFacade.getPubKey());
|
||||
|
||||
storage.write(user.getClass().getName(), user);
|
||||
processStepBar.next();
|
||||
buildStep2();
|
||||
} catch (InsufficientMoneyException e1)
|
||||
{
|
||||
Popups.openErrorPopup("Not enough money available", "There is not enough money available. Please pay in first to your wallet.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
skipButton.setOnAction(e -> close());
|
||||
//todo
|
||||
bankAccountTypesComboBox.getSelectionModel().select(0);
|
||||
currencyComboBox.getSelectionModel().select(0);
|
||||
countryComboBox.getSelectionModel().select(0);
|
||||
accountHolderName.setText("dummy accountHolderName");
|
||||
accountTitle.setText("dummy accountTitle");
|
||||
accountPrimaryID.setText("dummy accountPrimaryID");
|
||||
accountSecondaryID.setText("dummy accountSecondaryID");
|
||||
}
|
||||
|
||||
private void buildStep2()
|
||||
private void setupSettingsScreen()
|
||||
{
|
||||
vBox.getChildren().remove(infoLabel);
|
||||
vBox.getChildren().remove(nextButton);
|
||||
vBox.getChildren().remove(skipButton);
|
||||
//TODO
|
||||
}
|
||||
|
||||
gridPane.getChildren().clear();
|
||||
int row = -1;
|
||||
|
||||
FormBuilder.addHeaderLabel(gridPane, "Registration complete", ++row);
|
||||
FormBuilder.addTextField(gridPane, "Registration address:", walletFacade.getAccountRegistrationAddress().toString(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Balance:", BtcFormatter.formatSatoshis(walletFacade.getAccountRegistrationBalance(), true), ++row);
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Button closeButton = FormBuilder.addButton(gridPane, "Close", ++row);
|
||||
closeButton.setDefaultButton(true);
|
||||
closeButton.setOnAction(e -> close());
|
||||
|
||||
FormBuilder.addVSpacer(gridPane, ++row);
|
||||
|
||||
FormBuilder.addHeaderLabel(gridPane, "Summary", ++row);
|
||||
Label info = new Label("You have saved following bank accounts with your trading account to the blockchain:");
|
||||
gridPane.add(info, 0, ++row);
|
||||
GridPane.setColumnSpan(info, 3);
|
||||
|
||||
FormBuilder.addVSpacer(gridPane, ++row);
|
||||
|
||||
List<BankAccount> bankAccounts = user.getBankAccounts();
|
||||
Iterator<BankAccount> iterator = bankAccounts.iterator();
|
||||
int index = 0;
|
||||
while (iterator.hasNext())
|
||||
private void updateBalance(BigInteger balance)
|
||||
{
|
||||
if (balance.compareTo(BigInteger.ZERO) == 0)
|
||||
{
|
||||
FormBuilder.addHeaderLabel(gridPane, "Bank account " + (index + 1), ++row);
|
||||
BankAccount bankAccount = iterator.next();
|
||||
// need to get updated row from subroutine
|
||||
row = buildBankAccountDetails(bankAccount, ++row);
|
||||
FormBuilder.addVSpacer(gridPane, ++row);
|
||||
index++;
|
||||
confirmationLabel.setText("");
|
||||
progressIndicator.setOpacity(0);
|
||||
progressIndicator.setProgress(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
progressIndicator.setOpacity(1);
|
||||
progressIndicator.setProgress(-1);
|
||||
Set<Transaction> transactions = walletFacade.getAccountRegistrationWallet().getTransactions(false);
|
||||
for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
Transaction transaction = iterator.next();
|
||||
updateConfidence(transaction);
|
||||
break;
|
||||
}
|
||||
}
|
||||
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(balance));
|
||||
}
|
||||
|
||||
private void close()
|
||||
private void updateConfidence(Transaction tx)
|
||||
{
|
||||
walletFacade.removeRegistrationWalletListener(this);
|
||||
navigationController.navigateToView(NavigationController.HOME, "");
|
||||
TransactionConfidence confidence = tx.getConfidence();
|
||||
double progressIndicatorSize = 50;
|
||||
switch (confidence.getConfidenceType())
|
||||
{
|
||||
case UNKNOWN:
|
||||
confirmationLabel.setText("");
|
||||
progressIndicator.setProgress(0);
|
||||
break;
|
||||
case PENDING:
|
||||
confirmationLabel.setText("Seen by " + confidence.numBroadcastPeers() + " peer(s) / 0 confirmations");
|
||||
progressIndicator.setProgress(-1.0);
|
||||
progressIndicatorSize = 20;
|
||||
break;
|
||||
case BUILDING:
|
||||
confirmationLabel.setText("Confirmed in " + confidence.getDepthInBlocks() + " block(s)");
|
||||
progressIndicator.setProgress(Math.min(1, (double) confidence.getDepthInBlocks() / 6.0));
|
||||
break;
|
||||
case DEAD:
|
||||
confirmationLabel.setText("Transaction is invalid.");
|
||||
break;
|
||||
}
|
||||
|
||||
progressIndicator.setMaxHeight(progressIndicatorSize);
|
||||
progressIndicator.setPrefHeight(progressIndicatorSize);
|
||||
progressIndicator.setMaxWidth(progressIndicatorSize);
|
||||
progressIndicator.setPrefWidth(progressIndicatorSize);
|
||||
}
|
||||
|
||||
|
||||
// util
|
||||
private int buildBankAccountDetails(BankAccount bankAccount, int row)
|
||||
{
|
||||
FormBuilder.addTextField(gridPane, "Bank account holder name:", bankAccount.getAccountHolderName(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Bank account type", bankAccount.getBankAccountType().toString(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Bank account primary ID", bankAccount.getAccountPrimaryID(), ++row);
|
||||
FormBuilder.addTextField(gridPane, "Bank account secondary ID:", bankAccount.getAccountSecondaryID(), ++row);
|
||||
return row;
|
||||
}
|
||||
|
||||
// TODO need checks per bankTransferType
|
||||
private boolean verifyBankAccountData()
|
||||
{
|
||||
boolean accountIDsByBankTransferTypeValid = Verification.verifyAccountIDsByBankTransferType(bankTransferTypeComboBox.getSelectionModel().getSelectedItem(),
|
||||
accountPrimaryID.getText(),
|
||||
accountSecondaryID.getText());
|
||||
|
||||
return bankTransferTypeComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& countryComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& currencyComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& accountTitle.getText().length() > 0
|
||||
&& accountHolderName.getText().length() > 0
|
||||
&& accountPrimaryID.getText().length() > 0
|
||||
&& accountSecondaryID.getText().length() > 0
|
||||
&& accountIDsByBankTransferTypeValid;
|
||||
}
|
||||
|
||||
private void updateCreateAccountButton()
|
||||
{
|
||||
boolean funded = walletFacade.getAccountRegistrationBalance().compareTo(BigInteger.ZERO) > 0;
|
||||
nextButton.setDisable(!funded || walletFacade.getRegConfDepthInBlocks() == 0);
|
||||
}
|
||||
|
||||
|
||||
private void addBankAccount()
|
||||
{
|
||||
if (verifyBankAccountData())
|
||||
{
|
||||
BankAccount bankAccount = new BankAccount(
|
||||
bankTransferTypeComboBox.getSelectionModel().getSelectedItem(),
|
||||
bankAccountTypesComboBox.getSelectionModel().getSelectedItem(),
|
||||
currencyComboBox.getSelectionModel().getSelectedItem(),
|
||||
countryComboBox.getSelectionModel().getSelectedItem(),
|
||||
accountTitle.getText(),
|
||||
|
@ -427,8 +422,25 @@ public class SetupController implements Initializable, ChildController, WalletFa
|
|||
|
||||
private void checkCreateAccountButtonState()
|
||||
{
|
||||
nextButton.setDisable(!verifyBankAccountData());
|
||||
createAccountButton.setDisable(!verifyBankAccountData());
|
||||
addBankAccountButton.setDisable(!verifyBankAccountData());
|
||||
}
|
||||
|
||||
private boolean verifyBankAccountData()
|
||||
{
|
||||
boolean accountIDsByBankTransferTypeValid = Verification.verifyAccountIDsByBankTransferType(bankAccountTypesComboBox.getSelectionModel().getSelectedItem(),
|
||||
accountPrimaryID.getText(),
|
||||
accountSecondaryID.getText());
|
||||
|
||||
return bankAccountTypesComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& countryComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& currencyComboBox.getSelectionModel().getSelectedItem() != null
|
||||
&& accountTitle.getText().length() > 0
|
||||
&& accountHolderName.getText().length() > 0
|
||||
&& accountPrimaryID.getText().length() > 0
|
||||
&& accountSecondaryID.getText().length() > 0
|
||||
&& accountIDsByBankTransferTypeValid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +1,160 @@
|
|||
<?import io.bitsquare.gui.components.processbar.ProcessStepBar?>
|
||||
<?import io.bitsquare.gui.components.VSpacer?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<AnchorPane fx:id="rootContainer" fx:controller="io.bitsquare.gui.setup.SetupController"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
<ScrollPane fitToWidth="true" AnchorPane.leftAnchor="10" AnchorPane.rightAnchor="10" AnchorPane.topAnchor="10"
|
||||
AnchorPane.bottomAnchor="30">
|
||||
<VBox fx:id="vBox" spacing="10">
|
||||
<Label text="Setup trading account" id="headline-label"/>
|
||||
<ProcessStepBar fx:id="processStepBar"/>
|
||||
<VSpacer prefHeight="10"/>
|
||||
<Label fx:id="infoLabel"/>
|
||||
<GridPane fx:id="gridPane" vgap="5" hgap="5">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
|
||||
</padding>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT"/>
|
||||
<ColumnConstraints halignment="LEFT" prefWidth="320"/>
|
||||
<ColumnConstraints halignment="LEFT"/>
|
||||
</columnConstraints>
|
||||
</GridPane>
|
||||
<Button fx:id="nextButton" defaultButton="true"/>
|
||||
<Button fx:id="skipButton"/>
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
</AnchorPane>
|
||||
<AnchorPane fx:id="rootContainer" fx:controller="io.bitsquare.gui.setup.SetupController" prefWidth="800" prefHeight="600" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
|
||||
<Label id="headline-label" text="Setup trading account" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="10.0"/>
|
||||
|
||||
<Accordion fx:id="accordion" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
|
||||
<panes>
|
||||
|
||||
<TitledPane fx:id="payRegistrationFeePane" text="Pay registration fee">
|
||||
<content>
|
||||
<AnchorPane>
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label fx:id="payRegFeeInfoLabel"/>
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0">
|
||||
|
||||
<children>
|
||||
<Label text="Registration address:"/>
|
||||
<TextField fx:id="registrationAddressTextField" editable="false" GridPane.columnIndex="1"/>
|
||||
<Label fx:id="copyIcon" minWidth="10" GridPane.columnIndex="2">
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="0.0" right="0.0" top="-1.0"/>
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Copy address to clipboard"/>
|
||||
</tooltip>
|
||||
</Label>
|
||||
|
||||
<Label text="Balance:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="balanceTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<ProgressIndicator fx:id="progressIndicator" id="confidence-progress-indicator" GridPane.columnIndex="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="1" GridPane.rowSpan="2" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="2.0"/>
|
||||
</GridPane.margin>
|
||||
</ProgressIndicator>
|
||||
<Label fx:id="confirmationLabel" text="Checking confirmations..." GridPane.columnIndex="3" GridPane.rowIndex="1"/>
|
||||
|
||||
<Button defaultButton="true" onAction="#onPaymentDone" text="Payment done" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
<Button onAction="#onSkipPayment" text="Skip and pay later" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
</children>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES" prefWidth="20.0"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="SOMETIMES"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
|
||||
</GridPane>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</TitledPane>
|
||||
|
||||
|
||||
<TitledPane fx:id="addBankAccountPane" animated="true" text="Add Bank account">
|
||||
<content>
|
||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
|
||||
AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
|
||||
<Label fx:id="addBankAccountInfoLabel"/>
|
||||
|
||||
<GridPane hgap="5.0" vgap="5.0">
|
||||
|
||||
<children>
|
||||
<Label text="Bank account type:"/>
|
||||
<ComboBox fx:id="bankAccountTypesComboBox" promptText="Select bank account type" GridPane.columnIndex="1"/>
|
||||
|
||||
<Label text="Bank account title:" GridPane.rowIndex="1"/>
|
||||
<TextField fx:id="accountTitle" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
|
||||
<Label text="Bank account holder name" GridPane.rowIndex="2"/>
|
||||
<TextField fx:id="accountHolderName" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
|
||||
|
||||
<Label text="Bank account primary ID" GridPane.rowIndex="3"/>
|
||||
<TextField fx:id="accountPrimaryID" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||
|
||||
<Label text="Bank account secondary ID" GridPane.rowIndex="4"/>
|
||||
<TextField fx:id="accountSecondaryID" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
||||
|
||||
<Label text="Currency used for bank account:" GridPane.rowIndex="5"/>
|
||||
<ComboBox fx:id="currencyComboBox" promptText="Select currency" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
|
||||
|
||||
<Label text="Country of bank account" GridPane.rowIndex="6"/>
|
||||
<ComboBox fx:id="countryComboBox" promptText="Select country" GridPane.columnIndex="1" GridPane.rowIndex="6"/>
|
||||
|
||||
<Button fx:id="addBankAccountButton" onAction="#onAddBankAccount" text="Add another bank account" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||
|
||||
<Button fx:id="createAccountButton" defaultButton="true" onAction="#onCreateAccount" text="Create account" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="8"/>
|
||||
|
||||
<Button onAction="#onSkipBankAccountSetup" text="Skip and register later" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
|
||||
</children>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
<RowConstraints vgrow="SOMETIMES"/>
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</TitledPane>
|
||||
|
||||
<TitledPane fx:id="settingsPane" animated="true" text="Set up preferences">
|
||||
<content>
|
||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||
<children>
|
||||
<VBox spacing="20" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<children>
|
||||
<Label text="Set up preferences... TODO "/>
|
||||
<Button defaultButton="true" onAction="#onClose" text="Done" GridPane.columnIndex="1" GridPane.rowIndex="8"/>
|
||||
</children>
|
||||
</VBox>
|
||||
</children>
|
||||
</AnchorPane>
|
||||
</content>
|
||||
</TitledPane>
|
||||
|
||||
</panes>
|
||||
</Accordion>
|
||||
|
||||
</children>
|
||||
</AnchorPane>
|
||||
|
|
|
@ -13,9 +13,12 @@ public class Converter
|
|||
|
||||
public static double stringToDouble(String input)
|
||||
{
|
||||
if (input == null || input.equals(""))
|
||||
return 0;
|
||||
try
|
||||
{
|
||||
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault());
|
||||
Object d = decimalFormat.getDecimalFormatSymbols();
|
||||
return decimalFormat.parse(input).doubleValue();
|
||||
} catch (ParseException e)
|
||||
{
|
||||
|
|
|
@ -16,11 +16,11 @@ public class Icons
|
|||
public static final String MSG = "/images/nav/msg.png";
|
||||
public static final String SETTINGS = "/images/nav/settings.png";
|
||||
|
||||
public static final String MSG_ALERT = "/images/nav/alertRound.png";
|
||||
|
||||
public static final String BUY = "/images/buy.png";
|
||||
public static final String SELL = "/images/sell.png";
|
||||
public static final String REMOVE = "/images/remove_minus_9.png";
|
||||
public static final String ADD = "/images/list.png";
|
||||
public static final String REFRESH = "/images/refresh.png";
|
||||
public static final String REMOVE = "/images/removeOffer.png";
|
||||
|
||||
public static final String PROGRESS_0_ICON_FILE = "/images/tx/circleProgress0.png";
|
||||
public static final String PROGRESS_1_ICON_FILE = "/images/tx/circleProgress1.png";
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
package io.bitsquare.msg;
|
||||
|
||||
import io.bitsquare.trade.Contract;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.util.Utils;
|
||||
|
||||
public class Message
|
||||
{
|
||||
public final static String BROADCAST_NEW_OFFER = "BROADCAST_NEW_OFFER";
|
||||
public final static String REQUEST_TAKE_OFFER = "REQUEST_TAKE_OFFER";
|
||||
public final static String OFFER_ACCEPTED = "OFFER_ACCEPTED";
|
||||
public final static String REQUEST_OFFER_FEE_PAYMENT_CONFIRM = "REQUEST_OFFER_FEE_PAYMENT_CONFIRM";
|
||||
public final static String SEND_SIGNED_CONTRACT = "SEND_SIGNED_CONTRACT";
|
||||
|
||||
private String type;
|
||||
private Object payload;
|
||||
|
||||
|
||||
public Message(String type, String msg)
|
||||
{
|
||||
this.type = type;
|
||||
this.payload = msg;
|
||||
}
|
||||
|
||||
public Message(String type, Trade trade)
|
||||
{
|
||||
this.type = type;
|
||||
this.payload = trade;
|
||||
}
|
||||
|
||||
public Message(String type, Offer offer)
|
||||
{
|
||||
this.type = type;
|
||||
this.payload = offer;
|
||||
}
|
||||
|
||||
public Message(String type, Contract contract)
|
||||
{
|
||||
this.type = type;
|
||||
this.payload = contract;
|
||||
}
|
||||
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return type + ": " + Utils.objectToJson(payload);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
package io.bitsquare.msg;
|
||||
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.BitSquare;
|
||||
import io.bitsquare.msg.listeners.*;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.offerer.OffererPaymentProtocol;
|
||||
import io.bitsquare.trade.taker.TakerPaymentProtocol;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import net.tomp2p.connection.Bindings;
|
||||
import net.tomp2p.connection.PeerConnection;
|
||||
import net.tomp2p.futures.*;
|
||||
|
@ -21,10 +28,7 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* That facade delivers messaging functionality from the TomP2P library
|
||||
|
@ -38,15 +42,26 @@ public class MessageFacade
|
|||
public static final String PING = "ping";
|
||||
public static final String PONG = "pong";
|
||||
private static final int MASTER_PEER_PORT = 5000;
|
||||
private static String MASTER_PEER_IP = "127.0.0.1";
|
||||
private static String MASTER_PEER_IP = "192.168.1.33";
|
||||
|
||||
private Peer myPeerInstance;
|
||||
private Peer myPeer;
|
||||
private int port;
|
||||
private KeyPair keyPair;
|
||||
private Peer masterPeer;
|
||||
private PeerAddress otherPeerAddress;
|
||||
private PeerConnection peerConnection;
|
||||
private List<MessageListener> messageListeners = new ArrayList<>();
|
||||
private List<OrderBookListener> orderBookListeners = new ArrayList<>();
|
||||
private List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>();
|
||||
|
||||
// //TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols)
|
||||
private List<TakerPaymentProtocol> takerPaymentProtocols = new ArrayList<>();
|
||||
private List<OffererPaymentProtocol> offererPaymentProtocols = new ArrayList<>();
|
||||
|
||||
private List<PingPeerListener> pingPeerListeners = new ArrayList<>();
|
||||
|
||||
private Long lastTimeStamp = -3L;
|
||||
|
||||
|
||||
private BooleanProperty isDirty = new SimpleBooleanProperty(false);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -55,15 +70,15 @@ public class MessageFacade
|
|||
@Inject
|
||||
public MessageFacade()
|
||||
{
|
||||
try
|
||||
/* try
|
||||
{
|
||||
masterPeer = BootstrapMasterPeer.INSTANCE(MASTER_PEER_PORT);
|
||||
} catch (Exception e)
|
||||
{
|
||||
if (masterPeer != null)
|
||||
masterPeer.shutdown();
|
||||
log.info("masterPeer already instantiated by another app. " + e.getMessage());
|
||||
}
|
||||
System.err.println("masterPeer already instantiated by another app. " + e.getMessage());
|
||||
} */
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -72,35 +87,31 @@ public class MessageFacade
|
|||
|
||||
public void init()
|
||||
{
|
||||
String keyName = WalletFacade.WALLET_PREFIX;
|
||||
String keyName = BitSquare.ID;
|
||||
port = Bindings.MAX_PORT - Math.abs(new Random().nextInt()) % (Bindings.MAX_PORT - Bindings.MIN_DYN_PORT);
|
||||
if (WalletFacade.WALLET_PREFIX.equals("taker"))
|
||||
if (BitSquare.ID.equals("taker"))
|
||||
port = 4501;
|
||||
else if (WalletFacade.WALLET_PREFIX.equals("offerer"))
|
||||
else if (BitSquare.ID.equals("offerer"))
|
||||
port = 4500;
|
||||
|
||||
try
|
||||
{
|
||||
createMyPeerInstance(keyName, port);
|
||||
setupStorage();
|
||||
//setupStorage();
|
||||
//TODO save periodically or get informed if network address changes
|
||||
saveMyAddressToDHT();
|
||||
setupReplyHandler();
|
||||
} catch (IOException e)
|
||||
{
|
||||
shutDown();
|
||||
log.error("Error at setup peer" + e.getMessage());
|
||||
log.error("Error at setup myPeerInstance" + e.getMessage());
|
||||
}
|
||||
|
||||
//log.info("myPeerInstance knows: " + myPeerInstance.getPeerBean().getPeerMap().getAll());
|
||||
}
|
||||
|
||||
public void shutDown()
|
||||
{
|
||||
if (peerConnection != null)
|
||||
peerConnection.close();
|
||||
|
||||
if (myPeerInstance != null)
|
||||
myPeerInstance.shutdown();
|
||||
if (myPeer != null)
|
||||
myPeer.shutdown();
|
||||
|
||||
if (masterPeer != null)
|
||||
masterPeer.shutdown();
|
||||
|
@ -112,36 +123,40 @@ public class MessageFacade
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//TODO use Offer and do proper serialisation here
|
||||
public void publishOffer(String currency, Object offerObject) throws IOException
|
||||
public void addOffer(Offer offer) throws IOException
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currency);
|
||||
Data offerData = new Data(offerObject);
|
||||
offerData.setTTLSeconds(5);
|
||||
FutureDHT putFuture = myPeerInstance.add(locationKey).setData(offerData).start();
|
||||
putFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
Number160 contentKey = Number160.createHash(offer.getUid());
|
||||
final Data offerData = new Data(offer);
|
||||
//offerData.setTTLSeconds(5);
|
||||
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
|
||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> onOfferPublished(future.isSuccess()));
|
||||
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onOfferPublished(boolean success)
|
||||
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onOfferPublished(success);
|
||||
setDirty(locationKey);
|
||||
|
||||
for (OrderBookListener orderBookListener : orderBookListeners)
|
||||
orderBookListener.onOfferAdded(offerData, success);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Get offers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void getOffers(String currency)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currency);
|
||||
final FutureDHT getOffersFuture = myPeerInstance.get(locationKey).setAll().start();
|
||||
final Number160 locationKey = Number160.createHash(currency);
|
||||
final FutureDHT getOffersFuture = myPeer.get(locationKey).setAll().start();
|
||||
getOffersFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
|
@ -155,41 +170,130 @@ public class MessageFacade
|
|||
|
||||
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onOffersReceived(dataMap, success);
|
||||
for (OrderBookListener orderBookListener : orderBookListeners)
|
||||
orderBookListener.onOffersReceived(dataMap, success);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Remove offer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void removeOffer(String currency, Object offerObject) throws IOException
|
||||
public void removeOffer(Offer offer) throws IOException
|
||||
{
|
||||
Data offerData = new Data(offerObject);
|
||||
Number160 locationKey = Number160.createHash(currency);
|
||||
Number160 contentKey = offerData.getHash();
|
||||
FutureDHT putFuture = myPeerInstance.remove(locationKey).setContentKey(contentKey).start();
|
||||
putFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
Number160 contentKey = Number160.createHash(offer.getUid());
|
||||
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
|
||||
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> onOfferRemoved(future.isSuccess()));
|
||||
Data data = removeFuture.getData();
|
||||
Platform.runLater(() -> onOfferRemoved(data, future.isSuccess(), locationKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onOfferRemoved(boolean success)
|
||||
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onOfferRemoved(success);
|
||||
setDirty(locationKey);
|
||||
|
||||
for (OrderBookListener orderBookListener : orderBookListeners)
|
||||
orderBookListener.onOfferRemoved(data, success);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Check dirty flag for a location key
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BooleanProperty getIsDirtyProperty()
|
||||
{
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
public void getDirtyFlag(Currency currency) throws IOException
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currency.getCurrencyCode());
|
||||
FutureDHT getFuture = myPeer.get(getDirtyLocationKey(locationKey)).start();
|
||||
getFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Data data = getFuture.getData();
|
||||
if (data != null)
|
||||
{
|
||||
Object object = data.getObject();
|
||||
if (object instanceof Long)
|
||||
{
|
||||
final long lastTimeStamp = (Long) object;
|
||||
//System.out.println("getDirtyFlag " + lastTimeStamp);
|
||||
Platform.runLater(() -> onGetDirtyFlag(lastTimeStamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.out.println("getFuture exceptionCaught " + System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onGetDirtyFlag(long timeStamp)
|
||||
{
|
||||
// TODO don't get updates at first run....
|
||||
if (lastTimeStamp != timeStamp)
|
||||
{
|
||||
isDirty.setValue(!isDirty.get());
|
||||
}
|
||||
if (lastTimeStamp > 0)
|
||||
lastTimeStamp = timeStamp;
|
||||
else
|
||||
lastTimeStamp++;
|
||||
}
|
||||
|
||||
private Number160 getDirtyLocationKey(Number160 locationKey) throws IOException
|
||||
{
|
||||
return Number160.createHash(locationKey.toString() + "Dirty");
|
||||
}
|
||||
|
||||
private void setDirty(Number160 locationKey)
|
||||
{
|
||||
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
|
||||
lastTimeStamp = System.currentTimeMillis();
|
||||
FutureDHT putFuture = null;
|
||||
try
|
||||
{
|
||||
putFuture = myPeer.put(getDirtyLocationKey(locationKey)).setData(new Data(lastTimeStamp)).start();
|
||||
} catch (IOException e)
|
||||
{
|
||||
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
|
||||
}
|
||||
putFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
//System.out.println("operationComplete");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.err.println("exceptionCaught");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Send message
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean sendMessage(String message)
|
||||
/* public boolean sendMessage(Object message)
|
||||
{
|
||||
boolean result = false;
|
||||
if (otherPeerAddress != null)
|
||||
|
@ -197,10 +301,10 @@ public class MessageFacade
|
|||
if (peerConnection != null)
|
||||
peerConnection.close();
|
||||
|
||||
peerConnection = myPeerInstance.createPeerConnection(otherPeerAddress, 20);
|
||||
peerConnection = myPeer.createPeerConnection(otherPeerAddress, 20);
|
||||
if (!peerConnection.isClosed())
|
||||
{
|
||||
FutureResponse sendFuture = myPeerInstance.sendDirect(peerConnection).setObject(message).start();
|
||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(message).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
|
@ -222,8 +326,8 @@ public class MessageFacade
|
|||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} */
|
||||
/*
|
||||
private void onResponseFromSend(Object response)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
|
@ -235,34 +339,195 @@ public class MessageFacade
|
|||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onSendFailed();
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Find peer
|
||||
// Find peer address
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void findPeer(String pubKeyAsHex)
|
||||
public void getPeerAddress(final String pubKeyAsHex, AddressLookupListener listener)
|
||||
{
|
||||
final FutureDHT getPeerAddressFuture = myPeerInstance.get(getPubKeyHash(pubKeyAsHex)).start();
|
||||
final Number160 location = Number160.createHash(pubKeyAsHex);
|
||||
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
|
||||
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
|
||||
Platform.runLater(() -> onPeerFound(peerAddress));
|
||||
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
|
||||
Platform.runLater(() -> onAddressFound(peerAddress, listener));
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.runLater(() -> onGetPeerAddressFailed(listener));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onPeerFound(PeerAddress peerAddress)
|
||||
private void onAddressFound(final PeerAddress peerAddress, AddressLookupListener listener)
|
||||
{
|
||||
if (!peerAddress.equals(myPeerInstance.getPeerAddress()))
|
||||
listener.onResult(peerAddress);
|
||||
}
|
||||
|
||||
private void onGetPeerAddressFailed(AddressLookupListener listener)
|
||||
{
|
||||
listener.onFailed();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade process
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void sendTradeMessage(final PeerAddress peerAddress, final TradeMessage tradeMessage, TradeMessageListener listener)
|
||||
{
|
||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
||||
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
otherPeerAddress = peerAddress;
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onPeerFound();
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (sendFuture.isSuccess())
|
||||
{
|
||||
Platform.runLater(() -> onSendTradingMessageResult(listener));
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.runLater(() -> onSendTradingMessageFailed(listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void onSendTradingMessageResult(TradeMessageListener listener)
|
||||
{
|
||||
listener.onResult();
|
||||
}
|
||||
|
||||
private void onSendTradingMessageFailed(TradeMessageListener listener)
|
||||
{
|
||||
listener.onFailed();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Process incoming tradingMessage
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processTradingMessage(TradeMessage tradeMessage, PeerAddress sender)
|
||||
{
|
||||
//TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols)
|
||||
log.info("processTradingMessage " + tradeMessage.getType().toString());
|
||||
switch (tradeMessage.getType())
|
||||
{
|
||||
case REQUEST_TAKE_OFFER:
|
||||
// That is used to initiate the OffererPaymentProtocol and to show incoming requests in the view
|
||||
for (TakeOfferRequestListener takeOfferRequestListener : takeOfferRequestListeners)
|
||||
takeOfferRequestListener.onTakeOfferRequested(tradeMessage, sender);
|
||||
break;
|
||||
case ACCEPT_TAKE_OFFER_REQUEST:
|
||||
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
|
||||
takeOfferTradeListener.onTakeOfferRequestAccepted();
|
||||
break;
|
||||
case REJECT_TAKE_OFFER_REQUEST:
|
||||
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
|
||||
takeOfferTradeListener.onTakeOfferRequestRejected();
|
||||
break;
|
||||
case TAKE_OFFER_FEE_PAYED:
|
||||
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
|
||||
offererPaymentProtocol.onTakeOfferFeePayed(tradeMessage);
|
||||
break;
|
||||
case REQUEST_TAKER_DEPOSIT_PAYMENT:
|
||||
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
|
||||
takeOfferTradeListener.onTakerDepositPaymentRequested(tradeMessage);
|
||||
break;
|
||||
case REQUEST_OFFERER_DEPOSIT_PUBLICATION:
|
||||
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
|
||||
offererPaymentProtocol.onDepositTxReadyForPublication(tradeMessage);
|
||||
break;
|
||||
case DEPOSIT_TX_PUBLISHED:
|
||||
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
|
||||
takeOfferTradeListener.onDepositTxPublished(tradeMessage);
|
||||
break;
|
||||
case BANK_TX_INITED:
|
||||
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
|
||||
takeOfferTradeListener.onBankTransferInited();
|
||||
break;
|
||||
|
||||
default:
|
||||
log.info("default");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Ping peer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO not working anymore...
|
||||
public void pingPeer(String publicKeyAsHex)
|
||||
{
|
||||
Number160 location = Number160.createHash(publicKeyAsHex);
|
||||
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
|
||||
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
final Data data = getPeerAddressFuture.getData();
|
||||
if (data != null && data.getObject() instanceof PeerAddress)
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) data.getObject();
|
||||
Platform.runLater(() -> onAddressFoundPingPeer(peerAddress));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void onAddressFoundPingPeer(PeerAddress peerAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
||||
if (!peerConnection.isClosed())
|
||||
{
|
||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(PING).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (sendFuture.isSuccess())
|
||||
{
|
||||
final String pong = (String) sendFuture.getObject();
|
||||
if (pong != null)
|
||||
Platform.runLater(() -> onResponseFromPing(pong.equals(PONG)));
|
||||
}
|
||||
else
|
||||
{
|
||||
peerConnection.close();
|
||||
Platform.runLater(() -> onResponseFromPing(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
// ClosedChannelException can happen, check out if there is a better way to ping a myPeerInstance for online status
|
||||
}
|
||||
}
|
||||
|
||||
private void onResponseFromPing(boolean success)
|
||||
{
|
||||
for (PingPeerListener pingPeerListener : pingPeerListeners)
|
||||
pingPeerListener.onPingPeerResult(success);
|
||||
}
|
||||
|
||||
|
||||
|
@ -270,15 +535,6 @@ public class MessageFacade
|
|||
// Misc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public boolean isOtherPeerDefined()
|
||||
{
|
||||
return otherPeerAddress != null;
|
||||
}
|
||||
|
||||
public String getPubKeyAsHex()
|
||||
{
|
||||
return Utils.bytesToHexString(keyPair.getPublic().getEncoded());
|
||||
}
|
||||
|
||||
public PublicKey getPubKey()
|
||||
{
|
||||
|
@ -289,14 +545,54 @@ public class MessageFacade
|
|||
// Event Listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addMessageListener(MessageListener listener)
|
||||
public void addMessageListener(OrderBookListener listener)
|
||||
{
|
||||
messageListeners.add(listener);
|
||||
orderBookListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeMessageListener(MessageListener listener)
|
||||
public void removeMessageListener(OrderBookListener listener)
|
||||
{
|
||||
messageListeners.remove(listener);
|
||||
orderBookListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void addTakeOfferRequestListener(TakeOfferRequestListener listener)
|
||||
{
|
||||
takeOfferRequestListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeTakeOfferRequestListener(TakeOfferRequestListener listener)
|
||||
{
|
||||
takeOfferRequestListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void addTakerPaymentProtocol(TakerPaymentProtocol listener)
|
||||
{
|
||||
takerPaymentProtocols.add(listener);
|
||||
}
|
||||
|
||||
public void removeTakerPaymentProtocol(TakerPaymentProtocol listener)
|
||||
{
|
||||
takerPaymentProtocols.remove(listener);
|
||||
}
|
||||
|
||||
public void addOffererPaymentProtocol(OffererPaymentProtocol listener)
|
||||
{
|
||||
offererPaymentProtocols.add(listener);
|
||||
}
|
||||
|
||||
public void removeOffererPaymentProtocol(OffererPaymentProtocol listener)
|
||||
{
|
||||
offererPaymentProtocols.remove(listener);
|
||||
}
|
||||
|
||||
public void addPingPeerListener(PingPeerListener listener)
|
||||
{
|
||||
pingPeerListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removePingPeerListener(PingPeerListener listener)
|
||||
{
|
||||
pingPeerListeners.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -306,43 +602,59 @@ public class MessageFacade
|
|||
|
||||
private void createMyPeerInstance(String keyName, int port) throws IOException
|
||||
{
|
||||
keyPair = MsgKeyUtil.getKeyPair(keyName);
|
||||
myPeerInstance = new PeerMaker(keyPair).setPorts(port).makeAndListen();
|
||||
|
||||
//TODO use list of multiple master bootstrap peers
|
||||
/*PeerAddress bootstrapServerPeerAddress = new PeerAddress(BootstrapMasterPeer.ID, new InetSocketAddress(InetAddress.getByName(MASTER_PEER_IP), port));
|
||||
FutureBootstrap futureBootstrap = myPeerInstance.bootstrap().setPeerAddress(bootstrapServerPeerAddress).start();
|
||||
*/
|
||||
FutureBootstrap futureBootstrap = myPeerInstance.bootstrap().setBroadcast().setPorts(MASTER_PEER_PORT).start();
|
||||
if (futureBootstrap != null)
|
||||
keyPair = DSAKeyUtil.getKeyPair(keyName);
|
||||
myPeer = new PeerMaker(keyPair).setPorts(port).makeAndListen();
|
||||
final FutureBootstrap futureBootstrap = myPeer.bootstrap().setBroadcast().setPorts(MASTER_PEER_PORT).start();
|
||||
// futureBootstrap.awaitUninterruptibly();
|
||||
futureBootstrap.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
futureBootstrap.awaitUninterruptibly();
|
||||
if (futureBootstrap.getBootstrapTo() != null)
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
PeerAddress peerAddress = futureBootstrap.getBootstrapTo().iterator().next();
|
||||
myPeerInstance.discover().setPeerAddress(peerAddress).start().awaitUninterruptibly();
|
||||
if (futureBootstrap.getBootstrapTo() != null)
|
||||
{
|
||||
PeerAddress masterPeerAddress = futureBootstrap.getBootstrapTo().iterator().next();
|
||||
final FutureDiscover futureDiscover = myPeer.discover().setPeerAddress(masterPeerAddress).start();
|
||||
//futureDiscover.awaitUninterruptibly();
|
||||
futureDiscover.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
//System.out.println("operationComplete");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.err.println("exceptionCaught");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupStorage() throws IOException
|
||||
{
|
||||
//TODO WalletFacade.WALLET_PREFIX just temp...
|
||||
String dirPath = io.bitsquare.util.Utils.getRootDir() + "tomP2P_" + WalletFacade.WALLET_PREFIX;
|
||||
//TODO BitSquare.ID just temp...
|
||||
String dirPath = Utilities.getRootDir() + "tomP2P_" + BitSquare.ID;
|
||||
File dirFile = new File(dirPath);
|
||||
boolean success = true;
|
||||
if (!dirFile.exists())
|
||||
success = dirFile.mkdir();
|
||||
|
||||
if (success)
|
||||
myPeerInstance.getPeerBean().setStorage(new StorageDisk(dirPath));
|
||||
myPeer.getPeerBean().setStorage(new StorageDisk(dirPath));
|
||||
else
|
||||
log.warn("Unable to create directory " + dirPath);
|
||||
}
|
||||
|
||||
private void saveMyAddressToDHT() throws IOException
|
||||
{
|
||||
myPeerInstance.put(getPubKeyHash(getPubKeyAsHex())).setData(new Data(myPeerInstance.getPeerAddress())).start();
|
||||
Number160 location = Number160.createHash(DSAKeyUtil.getHexStringFromPublicKey(getPubKey()));
|
||||
//log.debug("saveMyAddressToDHT location "+location.toString());
|
||||
myPeer.put(location).setData(new Data(myPeer.getPeerAddress())).start();
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,47 +664,32 @@ public class MessageFacade
|
|||
|
||||
private void setupReplyHandler()
|
||||
{
|
||||
myPeerInstance.setObjectDataReply(new ObjectDataReply()
|
||||
myPeer.setObjectDataReply(new ObjectDataReply()
|
||||
{
|
||||
@Override
|
||||
public Object reply(PeerAddress sender, Object request) throws Exception
|
||||
{
|
||||
String reply = null;
|
||||
if (!sender.equals(myPeerInstance.getPeerAddress()))
|
||||
if (!sender.equals(myPeer.getPeerAddress()))
|
||||
{
|
||||
otherPeerAddress = sender;
|
||||
|
||||
Platform.runLater(() -> onMessage(request));
|
||||
if (request.equals(PING))
|
||||
{
|
||||
Platform.runLater(() -> onPing());
|
||||
}
|
||||
Platform.runLater(() -> onMessage(request, sender));
|
||||
}
|
||||
return reply;
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onMessage(Object message)
|
||||
private void onMessage(Object request, PeerAddress sender)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onMessage(message);
|
||||
if (request instanceof TradeMessage)
|
||||
{
|
||||
processTradingMessage((TradeMessage) request, sender);
|
||||
}
|
||||
/* else
|
||||
{
|
||||
for (OrderBookListener orderBookListener : orderBookListeners)
|
||||
orderBookListener.onMessage(request);
|
||||
} */
|
||||
}
|
||||
|
||||
private void onPing()
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onPing();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Number160 getPubKeyHash(String pubKeyAsHex)
|
||||
{
|
||||
return net.tomp2p.utils.Utils.makeSHAHash(pubKeyAsHex);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package io.bitsquare.msg;
|
||||
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.util.EventListener;
|
||||
import java.util.Map;
|
||||
|
||||
public interface MessageListener extends EventListener
|
||||
{
|
||||
void onMessage(Object message);
|
||||
|
||||
void onPing();
|
||||
|
||||
void onOfferPublished(boolean success);
|
||||
|
||||
void onSendFailed();
|
||||
|
||||
void onResponseFromSend(Object response);
|
||||
|
||||
void onPeerFound();
|
||||
|
||||
void onOffersReceived(Map<Number160, Data> dataMap, boolean success);
|
||||
|
||||
void onOfferRemoved(boolean success);
|
||||
}
|
181
src/main/java/io/bitsquare/msg/TradeMessage.java
Normal file
|
@ -0,0 +1,181 @@
|
|||
package io.bitsquare.msg;
|
||||
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TradeMessage implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 7916445031849763995L;
|
||||
|
||||
private String uid;
|
||||
|
||||
private String takerMessagePubKey;
|
||||
private String signedTakerDepositTxAsHex;
|
||||
private String txScriptSigAsHex;
|
||||
private String txConnOutAsHex;
|
||||
private String contractAsJson;
|
||||
private String takerContractSignature;
|
||||
private TradeMessageType type;
|
||||
private String depositTxID;
|
||||
private BigInteger tradeAmount;
|
||||
private String takeOfferFeeTxID;
|
||||
private String takerMultiSigPubKey;
|
||||
private String offerUID;
|
||||
private BankAccount bankAccount;
|
||||
private String accountID;
|
||||
private String offererPubKey;
|
||||
private String preparedOffererDepositTxAsHex;
|
||||
|
||||
public TradeMessage(TradeMessageType type, String offerUID)
|
||||
{
|
||||
this.offerUID = offerUID;
|
||||
this.type = type;
|
||||
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public TradeMessage(TradeMessageType type, String offerUID, BigInteger tradeAmount, String takeOfferFeeTxID, String takerMultiSigPubKey)
|
||||
{
|
||||
this.offerUID = offerUID;
|
||||
this.type = type;
|
||||
this.tradeAmount = tradeAmount;
|
||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||
this.takerMultiSigPubKey = takerMultiSigPubKey;
|
||||
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public TradeMessage(TradeMessageType type, String offerUID, BankAccount bankAccount, String accountID, String offererPubKey, String preparedOffererDepositTxAsHex)
|
||||
{
|
||||
this.offerUID = offerUID;
|
||||
this.type = type;
|
||||
this.bankAccount = bankAccount;
|
||||
this.accountID = accountID;
|
||||
this.offererPubKey = offererPubKey;
|
||||
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
|
||||
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public TradeMessage(TradeMessageType type, String offerUID,
|
||||
BankAccount bankAccount,
|
||||
String accountID,
|
||||
String takerMessagePubKey,
|
||||
String signedTakerDepositTxAsHex,
|
||||
String txScriptSigAsHex,
|
||||
String txConnOutAsHex,
|
||||
String contractAsJson,
|
||||
String takerContractSignature)
|
||||
{
|
||||
this.offerUID = offerUID;
|
||||
this.type = type;
|
||||
this.bankAccount = bankAccount;
|
||||
this.accountID = accountID;
|
||||
this.takerMessagePubKey = takerMessagePubKey;
|
||||
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
||||
this.txScriptSigAsHex = txScriptSigAsHex;
|
||||
this.txConnOutAsHex = txConnOutAsHex;
|
||||
this.contractAsJson = contractAsJson;
|
||||
this.takerContractSignature = takerContractSignature;
|
||||
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public TradeMessage(TradeMessageType type, String offerUID, String depositTxID)
|
||||
{
|
||||
this.offerUID = offerUID;
|
||||
this.type = type;
|
||||
this.depositTxID = depositTxID;
|
||||
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
public String getUid()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
public TradeMessageType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getTakeOfferFeeTxID()
|
||||
{
|
||||
return takeOfferFeeTxID;
|
||||
}
|
||||
|
||||
public String getOfferUID()
|
||||
{
|
||||
return offerUID;
|
||||
}
|
||||
|
||||
public BankAccount getBankAccount()
|
||||
{
|
||||
return bankAccount;
|
||||
}
|
||||
|
||||
public String getAccountID()
|
||||
{
|
||||
return accountID;
|
||||
}
|
||||
|
||||
public String getTakerMultiSigPubKey()
|
||||
{
|
||||
return takerMultiSigPubKey;
|
||||
}
|
||||
|
||||
public String getPreparedOffererDepositTxAsHex()
|
||||
{
|
||||
return preparedOffererDepositTxAsHex;
|
||||
}
|
||||
|
||||
public BigInteger getTradeAmount()
|
||||
{
|
||||
return tradeAmount;
|
||||
}
|
||||
|
||||
public String getTakerMessagePubKey()
|
||||
{
|
||||
return takerMessagePubKey;
|
||||
}
|
||||
|
||||
public String getSignedTakerDepositTxAsHex()
|
||||
{
|
||||
return signedTakerDepositTxAsHex;
|
||||
}
|
||||
|
||||
public String getContractAsJson()
|
||||
{
|
||||
return contractAsJson;
|
||||
}
|
||||
|
||||
public String getTakerContractSignature()
|
||||
{
|
||||
return takerContractSignature;
|
||||
}
|
||||
|
||||
public String getTxScriptSigAsHex()
|
||||
{
|
||||
return txScriptSigAsHex;
|
||||
}
|
||||
|
||||
public String getTxConnOutAsHex()
|
||||
{
|
||||
return txConnOutAsHex;
|
||||
}
|
||||
|
||||
public String getDepositTxID()
|
||||
{
|
||||
return depositTxID;
|
||||
}
|
||||
|
||||
public String getOffererPubKey()
|
||||
{
|
||||
return offererPubKey;
|
||||
}
|
||||
}
|
13
src/main/java/io/bitsquare/msg/TradeMessageType.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package io.bitsquare.msg;
|
||||
|
||||
public enum TradeMessageType
|
||||
{
|
||||
REQUEST_TAKE_OFFER,
|
||||
ACCEPT_TAKE_OFFER_REQUEST,
|
||||
REJECT_TAKE_OFFER_REQUEST,
|
||||
TAKE_OFFER_FEE_PAYED,
|
||||
REQUEST_TAKER_DEPOSIT_PAYMENT,
|
||||
REQUEST_OFFERER_DEPOSIT_PUBLICATION,
|
||||
DEPOSIT_TX_PUBLISHED,
|
||||
BANK_TX_INITED
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.bitsquare.msg.listeners;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
public interface AddressLookupListener
|
||||
{
|
||||
void onResult(PeerAddress peerAddress);
|
||||
|
||||
void onFailed();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.bitsquare.msg.listeners;
|
||||
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface OrderBookListener
|
||||
{
|
||||
void onOfferAdded(Data offerData, boolean success);
|
||||
|
||||
void onOffersReceived(Map<Number160, Data> dataMap, boolean success);
|
||||
|
||||
void onOfferRemoved(Data data, boolean success);
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.bitsquare.msg.listeners;
|
||||
|
||||
public interface PingPeerListener
|
||||
{
|
||||
void onPing();
|
||||
|
||||
void onPingPeerResult(boolean success);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package io.bitsquare.msg.listeners;
|
||||
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
public interface TakeOfferRequestListener
|
||||
{
|
||||
void onTakeOfferRequested(TradeMessage tradeMessage, PeerAddress sender);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.bitsquare.msg.listeners;
|
||||
|
||||
public interface TradeMessageListener
|
||||
{
|
||||
void onFailed();
|
||||
|
||||
void onResult();
|
||||
}
|
|
@ -16,8 +16,8 @@ public class Settings implements Serializable
|
|||
private List<Locale> acceptedLanguageLocales = new ArrayList<>();
|
||||
private List<Locale> acceptedCountryLocales = new ArrayList<>();
|
||||
private List<Arbitrator> acceptedArbitrators = new ArrayList<>();
|
||||
private double maxCollateral;
|
||||
private double minCollateral;
|
||||
private int maxCollateral;
|
||||
private int minCollateral;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -63,12 +63,12 @@ public class Settings implements Serializable
|
|||
acceptedArbitrators.add(arbitrator);
|
||||
}
|
||||
|
||||
public void setMaxCollateral(double maxCollateral)
|
||||
public void setMaxCollateral(int maxCollateral)
|
||||
{
|
||||
this.maxCollateral = maxCollateral;
|
||||
}
|
||||
|
||||
public void setMinCollateral(double minCollateral)
|
||||
public void setMinCollateral(int minCollateral)
|
||||
{
|
||||
this.minCollateral = minCollateral;
|
||||
}
|
||||
|
@ -92,26 +92,27 @@ public class Settings implements Serializable
|
|||
return acceptedCountryLocales;
|
||||
}
|
||||
|
||||
public Arbitrator getRandomArbitrator(double collateral, BigInteger amount)
|
||||
//TODO
|
||||
public Arbitrator getRandomArbitrator(int collateral, BigInteger amount)
|
||||
{
|
||||
List<Arbitrator> candidates = new ArrayList<>();
|
||||
for (Arbitrator arbitrator : acceptedArbitrators)
|
||||
{
|
||||
if (arbitrator.getArbitrationFeePercent() >= collateral &&
|
||||
arbitrator.getMinArbitrationFee().compareTo(amount) < 0)
|
||||
{
|
||||
candidates.add(arbitrator);
|
||||
}
|
||||
/*if (arbitrator.getArbitrationFeePercent() >= collateral &&
|
||||
arbitrator.getMinArbitrationAmount().compareTo(amount) < 0)
|
||||
{ */
|
||||
candidates.add(arbitrator);
|
||||
// }
|
||||
}
|
||||
return candidates.size() > 0 ? candidates.get((int) (Math.random() * candidates.size())) : null;
|
||||
}
|
||||
|
||||
public double getMaxCollateral()
|
||||
public int getMaxCollateral()
|
||||
{
|
||||
return maxCollateral;
|
||||
}
|
||||
|
||||
public double getMinCollateral()
|
||||
public int getMinCollateral()
|
||||
{
|
||||
return minCollateral;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.bitsquare.storage;
|
||||
|
||||
import io.bitsquare.util.Utils;
|
||||
import io.bitsquare.BitSquare;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -16,7 +17,7 @@ public class Storage
|
|||
private static final Logger log = LoggerFactory.getLogger(Storage.class);
|
||||
|
||||
//TODO save in users preferences location
|
||||
private final String preferencesFileName = "preferences.ser";
|
||||
private final String preferencesFileName = "pref_" + BitSquare.ID + ".ser";
|
||||
private final String storageFile;
|
||||
private Map<String, Object> dict;
|
||||
|
||||
|
@ -27,7 +28,7 @@ public class Storage
|
|||
|
||||
public Storage()
|
||||
{
|
||||
storageFile = Utils.getRootDir() + preferencesFileName;
|
||||
storageFile = Utilities.getRootDir() + preferencesFileName;
|
||||
|
||||
dict = readDataVO();
|
||||
if (dict == null)
|
||||
|
|
|
@ -1,61 +1,107 @@
|
|||
package io.bitsquare.trade;
|
||||
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Contract
|
||||
public class Contract implements Serializable
|
||||
{
|
||||
private User taker;
|
||||
private Trade trade;
|
||||
private String takerPubKey;
|
||||
private String offererPubKey;
|
||||
private static final long serialVersionUID = 71472356206100158L;
|
||||
|
||||
public Contract(User taker, Trade trade, String takerPubKey)
|
||||
private Offer offer;
|
||||
private String takeOfferFeeTxID;
|
||||
private BigInteger tradeAmount;
|
||||
private String offererAccountID;
|
||||
private String takerAccountID;
|
||||
private BankAccount offererBankAccount;
|
||||
private BankAccount takerBankAccount;
|
||||
private String offererMessagePubKeyAsHex;
|
||||
private String takerMessagePubKeyAsHex;
|
||||
|
||||
public Contract(Offer offer,
|
||||
BigInteger tradeAmount,
|
||||
String takeOfferFeeTxID,
|
||||
String offererAccountID,
|
||||
String takerAccountID,
|
||||
BankAccount offererBankAccount,
|
||||
BankAccount takerBankAccount,
|
||||
String offererMessagePubKeyAsHex,
|
||||
String takerMessagePubKeyAsHex)
|
||||
{
|
||||
this.taker = taker;
|
||||
this.trade = trade;
|
||||
this.takerPubKey = takerPubKey;
|
||||
this.offer = offer;
|
||||
this.tradeAmount = tradeAmount;
|
||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||
this.offererAccountID = offererAccountID;
|
||||
this.takerAccountID = takerAccountID;
|
||||
this.offererBankAccount = offererBankAccount;
|
||||
this.takerBankAccount = takerBankAccount;
|
||||
this.offererMessagePubKeyAsHex = offererMessagePubKeyAsHex;
|
||||
this.takerMessagePubKeyAsHex = takerMessagePubKeyAsHex;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Setters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setOffererPubKey(String offererPubKey)
|
||||
{
|
||||
this.offererPubKey = offererPubKey;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public UUID getUid()
|
||||
public Offer getOffer()
|
||||
{
|
||||
return trade.getUid();
|
||||
return offer;
|
||||
}
|
||||
|
||||
public User getTaker()
|
||||
public String getTakeOfferFeeTxID()
|
||||
{
|
||||
return taker;
|
||||
return takeOfferFeeTxID;
|
||||
}
|
||||
|
||||
public String getTakerPubKey()
|
||||
public BigInteger getTradeAmount()
|
||||
{
|
||||
return takerPubKey;
|
||||
return tradeAmount;
|
||||
}
|
||||
|
||||
public Trade getTrade()
|
||||
public String getOffererAccountID()
|
||||
{
|
||||
return trade;
|
||||
return offererAccountID;
|
||||
}
|
||||
|
||||
public String getOffererPubKey()
|
||||
public String getTakerAccountID()
|
||||
{
|
||||
return offererPubKey;
|
||||
return takerAccountID;
|
||||
}
|
||||
|
||||
public BankAccount getOffererBankAccount()
|
||||
{
|
||||
return offererBankAccount;
|
||||
}
|
||||
|
||||
public BankAccount getTakerBankAccount()
|
||||
{
|
||||
return takerBankAccount;
|
||||
}
|
||||
|
||||
public String getTakerMessagePubKeyAsHex()
|
||||
{
|
||||
return takerMessagePubKeyAsHex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Contract{" +
|
||||
"offer=" + offer +
|
||||
", takeOfferFeeTxID='" + takeOfferFeeTxID + '\'' +
|
||||
", tradeAmount=" + tradeAmount +
|
||||
", offererAccountID='" + offererAccountID + '\'' +
|
||||
", takerAccountID='" + takerAccountID + '\'' +
|
||||
", offererBankAccount=" + offererBankAccount +
|
||||
", takerBankAccount=" + takerBankAccount +
|
||||
", offererMessagePubKeyAsHex='" + offererMessagePubKeyAsHex + '\'' +
|
||||
", takerMessagePubKeyAsHex='" + takerMessagePubKeyAsHex + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getOffererMessagePubKeyAsHex()
|
||||
{
|
||||
return offererMessagePubKeyAsHex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,38 @@
|
|||
package io.bitsquare.trade;
|
||||
|
||||
import io.bitsquare.bank.BankAccountType;
|
||||
import io.bitsquare.btc.BtcFormatter;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Offer
|
||||
public class Offer implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = -971164804305475826L;
|
||||
|
||||
// key attributes for lookup
|
||||
private Direction direction;
|
||||
private Currency currency;
|
||||
|
||||
private UUID uid;
|
||||
private String uid;
|
||||
|
||||
private double price;
|
||||
private BigInteger amount;
|
||||
private BigInteger minAmount;
|
||||
private String accountID;
|
||||
private String messageID;
|
||||
private String messagePubKeyAsHex;
|
||||
private BankAccountType.BankAccountTypeEnum bankAccountTypeEnum;
|
||||
private Locale bankAccountCountryLocale;
|
||||
|
||||
private double collateral;
|
||||
private int collateral;
|
||||
private List<Locale> acceptedCountryLocales;
|
||||
private List<Locale> acceptedLanguageLocales;
|
||||
private String offerPaymentTxID;
|
||||
private String offerFeePaymentTxID;
|
||||
private String bankAccountUID;
|
||||
private Arbitrator arbitrator;
|
||||
|
||||
|
||||
|
@ -36,8 +40,7 @@ public class Offer
|
|||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Offer(String accountID,
|
||||
String messageID,
|
||||
public Offer(String messagePubKeyAsHex,
|
||||
Direction direction,
|
||||
double price,
|
||||
BigInteger amount,
|
||||
|
@ -45,13 +48,13 @@ public class Offer
|
|||
BankAccountType.BankAccountTypeEnum bankAccountTypeEnum,
|
||||
Currency currency,
|
||||
Locale bankAccountCountryLocale,
|
||||
String bankAccountUID,
|
||||
Arbitrator arbitrator,
|
||||
double collateral,
|
||||
int collateral,
|
||||
List<Locale> acceptedCountryLocales,
|
||||
List<Locale> acceptedLanguageLocales)
|
||||
{
|
||||
this.accountID = accountID;
|
||||
this.messageID = messageID;
|
||||
this.messagePubKeyAsHex = messagePubKeyAsHex;
|
||||
this.direction = direction;
|
||||
this.price = price;
|
||||
this.amount = amount;
|
||||
|
@ -59,12 +62,13 @@ public class Offer
|
|||
this.bankAccountTypeEnum = bankAccountTypeEnum;
|
||||
this.currency = currency;
|
||||
this.bankAccountCountryLocale = bankAccountCountryLocale;
|
||||
this.bankAccountUID = bankAccountUID;
|
||||
this.arbitrator = arbitrator;
|
||||
this.collateral = collateral;
|
||||
this.acceptedCountryLocales = acceptedCountryLocales;
|
||||
this.acceptedLanguageLocales = acceptedLanguageLocales;
|
||||
|
||||
uid = UUID.randomUUID();
|
||||
this.uid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
|
||||
|
@ -72,9 +76,9 @@ public class Offer
|
|||
// Setters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setOfferPaymentTxID(String offerPaymentTxID)
|
||||
public void setOfferFeePaymentTxID(String offerFeePaymentTxID)
|
||||
{
|
||||
this.offerPaymentTxID = offerPaymentTxID;
|
||||
this.offerFeePaymentTxID = offerFeePaymentTxID;
|
||||
}
|
||||
|
||||
|
||||
|
@ -82,17 +86,12 @@ public class Offer
|
|||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getAccountID()
|
||||
public String getMessagePubKeyAsHex()
|
||||
{
|
||||
return accountID;
|
||||
return messagePubKeyAsHex;
|
||||
}
|
||||
|
||||
public String getMessageID()
|
||||
{
|
||||
return messageID;
|
||||
}
|
||||
|
||||
public UUID getUid()
|
||||
public String getUid()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
@ -144,17 +143,17 @@ public class Offer
|
|||
|
||||
public double getVolume()
|
||||
{
|
||||
return price * amount.doubleValue();
|
||||
return price * BtcFormatter.satoshiToBTC(amount);
|
||||
}
|
||||
|
||||
public double getMinVolume()
|
||||
{
|
||||
return price * minAmount.doubleValue();
|
||||
return price * BtcFormatter.satoshiToBTC(minAmount);
|
||||
}
|
||||
|
||||
public String getOfferPaymentTxID()
|
||||
public String getOfferFeePaymentTxID()
|
||||
{
|
||||
return offerPaymentTxID;
|
||||
return offerFeePaymentTxID;
|
||||
}
|
||||
|
||||
public Arbitrator getArbitrator()
|
||||
|
@ -162,8 +161,35 @@ public class Offer
|
|||
return arbitrator;
|
||||
}
|
||||
|
||||
public double getCollateral()
|
||||
public int getCollateral()
|
||||
{
|
||||
return collateral;
|
||||
}
|
||||
|
||||
public String getBankAccountUID()
|
||||
{
|
||||
return bankAccountUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Offer{" +
|
||||
"direction=" + direction +
|
||||
", currency=" + currency +
|
||||
", uid='" + uid + '\'' +
|
||||
", price=" + price +
|
||||
", amount=" + amount +
|
||||
", minAmount=" + minAmount +
|
||||
", messagePubKey=" + messagePubKeyAsHex.hashCode() +
|
||||
", bankAccountTypeEnum=" + bankAccountTypeEnum +
|
||||
", bankAccountCountryLocale=" + bankAccountCountryLocale +
|
||||
", collateral=" + collateral +
|
||||
", acceptedCountryLocales=" + acceptedCountryLocales +
|
||||
", acceptedLanguageLocales=" + acceptedLanguageLocales +
|
||||
", offerFeePaymentTxID='" + offerFeePaymentTxID + '\'' +
|
||||
", bankAccountUID='" + bankAccountUID + '\'' +
|
||||
", arbitrator=" + arbitrator +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +1,131 @@
|
|||
package io.bitsquare.trade;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.UUID;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
|
||||
public class Trade
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Trade implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = -8275323072940974077L;
|
||||
|
||||
transient private final SimpleBooleanProperty depositTxChangedProperty = new SimpleBooleanProperty();
|
||||
|
||||
private Offer offer;
|
||||
private boolean takeOfferRequested;
|
||||
private boolean takeOfferAccepted;
|
||||
private BigInteger requestedAmount;
|
||||
private boolean takeOfferFeePaymentConfirmed;
|
||||
private String jsonRepresentation;
|
||||
private String signature;
|
||||
private String takeOfferFeeTxID;
|
||||
private BigInteger tradeAmount;
|
||||
private Contract contract;
|
||||
private String contractAsJson;
|
||||
private String takerSignature;
|
||||
private Transaction depositTransaction;
|
||||
|
||||
public Trade(Offer offer)
|
||||
{
|
||||
this.offer = offer;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Setters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
|
||||
{
|
||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||
}
|
||||
|
||||
public void setTradeAmount(BigInteger tradeAmount)
|
||||
{
|
||||
this.tradeAmount = tradeAmount;
|
||||
}
|
||||
|
||||
public void setContract(Contract contract)
|
||||
{
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
public void setContractAsJson(String contractAsJson)
|
||||
{
|
||||
this.contractAsJson = contractAsJson;
|
||||
}
|
||||
|
||||
public void setContractTakerSignature(String takerSignature)
|
||||
{
|
||||
this.takerSignature = takerSignature;
|
||||
}
|
||||
|
||||
public void setDepositTransaction(Transaction depositTransaction)
|
||||
{
|
||||
this.depositTransaction = depositTransaction;
|
||||
depositTxChangedProperty.set(!depositTxChangedProperty.get());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getUid()
|
||||
{
|
||||
return offer.getUid();
|
||||
}
|
||||
|
||||
public Offer getOffer()
|
||||
{
|
||||
return offer;
|
||||
}
|
||||
|
||||
public UUID getUid()
|
||||
public String getTakeOfferFeeTxID()
|
||||
{
|
||||
return offer.getUid();
|
||||
return takeOfferFeeTxID;
|
||||
}
|
||||
|
||||
public void setJsonRepresentation(String jsonRepresentation)
|
||||
public BigInteger getTradeAmount()
|
||||
{
|
||||
this.jsonRepresentation = jsonRepresentation;
|
||||
return tradeAmount;
|
||||
}
|
||||
|
||||
public void setSignature(String signature)
|
||||
public Contract getContract()
|
||||
{
|
||||
this.signature = signature;
|
||||
return contract;
|
||||
}
|
||||
|
||||
public boolean isTakeOfferRequested()
|
||||
public String getContractAsJson()
|
||||
{
|
||||
return takeOfferRequested;
|
||||
return contractAsJson;
|
||||
}
|
||||
|
||||
public void setTakeOfferRequested(boolean takeOfferRequested)
|
||||
public String getTakerSignature()
|
||||
{
|
||||
this.takeOfferRequested = takeOfferRequested;
|
||||
return takerSignature;
|
||||
}
|
||||
|
||||
public boolean isTakeOfferAccepted()
|
||||
public Transaction getDepositTransaction()
|
||||
{
|
||||
return takeOfferAccepted;
|
||||
return depositTransaction;
|
||||
}
|
||||
|
||||
public void setTakeOfferAccepted(boolean takeOfferAccepted)
|
||||
public SimpleBooleanProperty getDepositTxChangedProperty()
|
||||
{
|
||||
this.takeOfferAccepted = takeOfferAccepted;
|
||||
return depositTxChangedProperty;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// toString
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Trade{" +
|
||||
"offer=" + offer +
|
||||
", takeOfferFeeTxID='" + takeOfferFeeTxID + '\'' +
|
||||
", tradeAmount=" + tradeAmount +
|
||||
", contract=" + contract +
|
||||
", contractAsJson='" + contractAsJson + '\'' +
|
||||
", takerSignature='" + takerSignature + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public BigInteger getRequestedAmount()
|
||||
{
|
||||
return requestedAmount;
|
||||
}
|
||||
|
||||
public void setTradeAmount(BigInteger requestedAmount)
|
||||
{
|
||||
this.requestedAmount = requestedAmount;
|
||||
}
|
||||
|
||||
public void setTakeOfferFeePaymentConfirmed(boolean takeOfferFeePaymentConfirmed)
|
||||
{
|
||||
this.takeOfferFeePaymentConfirmed = takeOfferFeePaymentConfirmed;
|
||||
}
|
||||
|
||||
public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
|
||||
{
|
||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,35 +2,46 @@ package io.bitsquare.trade;
|
|||
|
||||
import com.google.bitcoin.core.InsufficientMoneyException;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.btc.BlockChainFacade;
|
||||
import io.bitsquare.btc.Fees;
|
||||
import io.bitsquare.btc.KeyPair;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.crypto.CryptoFacade;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import io.bitsquare.settings.Settings;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.offerer.OffererPaymentProtocol;
|
||||
import io.bitsquare.trade.offerer.OffererPaymentProtocolListener;
|
||||
import io.bitsquare.trade.taker.TakerPaymentProtocol;
|
||||
import io.bitsquare.trade.taker.TakerPaymentProtocolListener;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utils;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents trade domain. Keeps complexity of process apart from view controller
|
||||
*/
|
||||
//TODO use scheduler/process pattern with tasks for every async job
|
||||
public class Trading
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(Trading.class);
|
||||
|
||||
private final HashMap<String, Offer> offers = new HashMap<>();
|
||||
private final HashMap<String, Trade> trades = new HashMap<>();
|
||||
private final HashMap<String, Contract> contracts = new HashMap<>();
|
||||
private Map<String, Offer> myOffers = new HashMap<>();
|
||||
private Map<String, Trade> trades = new HashMap<>();
|
||||
private final Map<String, TakerPaymentProtocol> takerPaymentProtocols = new HashMap<>();
|
||||
|
||||
|
||||
private final Map<String, OffererPaymentProtocol> offererPaymentProtocols = new HashMap<>();
|
||||
private final String storageKey;
|
||||
private User user;
|
||||
private Storage storage;
|
||||
private MessageFacade messageFacade;
|
||||
private BlockChainFacade blockChainFacade;
|
||||
private WalletFacade walletFacade;
|
||||
|
@ -45,6 +56,7 @@ public class Trading
|
|||
@Inject
|
||||
public Trading(User user,
|
||||
Settings settings,
|
||||
Storage storage,
|
||||
MessageFacade messageFacade,
|
||||
BlockChainFacade blockChainFacade,
|
||||
WalletFacade walletFacade,
|
||||
|
@ -52,10 +64,21 @@ public class Trading
|
|||
{
|
||||
this.user = user;
|
||||
this.settings = settings;
|
||||
this.storage = storage;
|
||||
this.messageFacade = messageFacade;
|
||||
this.blockChainFacade = blockChainFacade;
|
||||
this.walletFacade = walletFacade;
|
||||
this.cryptoFacade = cryptoFacade;
|
||||
|
||||
storageKey = this.getClass().getName();
|
||||
|
||||
Object offersObject = storage.read(storageKey + ".offers");
|
||||
if (offersObject != null && offersObject instanceof HashMap)
|
||||
myOffers = (Map) offersObject;
|
||||
|
||||
Object tradesObject = storage.read(storageKey + ".trades");
|
||||
if (tradesObject != null && tradesObject instanceof HashMap)
|
||||
trades = (Map) tradesObject;
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,79 +86,117 @@ public class Trading
|
|||
// Public Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Offer, trade, contract
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void placeNewOffer(Offer offer, FutureCallback<Transaction> callback) throws InsufficientMoneyException
|
||||
{
|
||||
log.info("place New Offer");
|
||||
offers.put(offer.getUid().toString(), offer);
|
||||
walletFacade.payFee(Fees.OFFER_CREATION_FEE, callback);
|
||||
if (myOffers.containsKey(offer.getUid()))
|
||||
throw new IllegalStateException("offers contains already a offer with the ID " + offer.getUid());
|
||||
|
||||
// messageFacade.broadcast(new Message(Message.BROADCAST_NEW_OFFER, offer));
|
||||
myOffers.put(offer.getUid(), offer);
|
||||
storage.write(storageKey + ".offers", myOffers);
|
||||
walletFacade.payFee(Fees.OFFER_CREATION_FEE, callback);
|
||||
}
|
||||
|
||||
public void removeOffer(Offer offer) throws IOException
|
||||
{
|
||||
myOffers.remove(offer.getUid());
|
||||
storage.write(storageKey + ".offers", myOffers);
|
||||
}
|
||||
|
||||
public Trade createTrade(Offer offer)
|
||||
{
|
||||
log.info("create New Trade");
|
||||
/* if (trades.containsKey(offer.getUid()))
|
||||
throw new IllegalStateException("trades contains already a trade with the ID " + offer.getUid()); */
|
||||
|
||||
Trade trade = new Trade(offer);
|
||||
trades.put(trade.getUid().toString(), trade);
|
||||
trades.put(offer.getUid(), trade);
|
||||
storage.write(storageKey + ".trades", trades);
|
||||
return trade;
|
||||
}
|
||||
|
||||
public Contract createContract(Trade trade)
|
||||
public void removeTrade(Trade trade) throws IOException
|
||||
{
|
||||
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;
|
||||
trades.remove(trade.getUid());
|
||||
storage.write(storageKey + ".trades", trades);
|
||||
}
|
||||
|
||||
public TakerPaymentProtocol addTakerPaymentProtocol(Trade trade, TakerPaymentProtocolListener listener)
|
||||
{
|
||||
TakerPaymentProtocol takerPaymentProtocol = new TakerPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
|
||||
takerPaymentProtocols.put(trade.getUid(), takerPaymentProtocol);
|
||||
return takerPaymentProtocol;
|
||||
}
|
||||
|
||||
public OffererPaymentProtocol addOffererPaymentProtocol(Trade trade, OffererPaymentProtocolListener listener)
|
||||
{
|
||||
OffererPaymentProtocol offererPaymentProtocol = new OffererPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
|
||||
offererPaymentProtocols.put(trade.getUid(), offererPaymentProtocol);
|
||||
return offererPaymentProtocol;
|
||||
}
|
||||
|
||||
public void createOffererPaymentProtocol(TradeMessage tradeMessage, PeerAddress sender)
|
||||
{
|
||||
Offer offer = myOffers.get(tradeMessage.getOfferUID());
|
||||
Trade trade = createTrade(offer);
|
||||
OffererPaymentProtocol offererPaymentProtocol = addOffererPaymentProtocol(trade, new OffererPaymentProtocolListener()
|
||||
{
|
||||
@Override
|
||||
public void onProgress(double progress)
|
||||
{
|
||||
//log.debug("onProgress " + progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(String failureMessage)
|
||||
{
|
||||
log.warn(failureMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDepositTxPublished(String depositTxID)
|
||||
{
|
||||
log.debug("trading onDepositTxPublished " + depositTxID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDepositTxConfirmedUpdate(TransactionConfidence confidence)
|
||||
{
|
||||
log.debug("trading onDepositTxConfirmedUpdate");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDepositTxConfirmedInBlockchain()
|
||||
{
|
||||
log.debug("trading onDepositTxConfirmedInBlockchain");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// the handler was not called there because the obejct was not created when the event occurred (and therefor no listener)
|
||||
// will probably created earlier, so let it for the moment like that....
|
||||
offererPaymentProtocol.onTakeOfferRequested(sender);
|
||||
}
|
||||
|
||||
|
||||
// trade process
|
||||
// 1
|
||||
public void sendTakeOfferRequest(Trade trade)
|
||||
public void onBankTransferInited(String tradeUID)
|
||||
{
|
||||
log.info("Taker asks offerer to take his offer");
|
||||
//messageFacade.send(new Message(Message.REQUEST_TAKE_OFFER, trade), trade.getOffer().getOfferer().getMessageID());
|
||||
offererPaymentProtocols.get(tradeUID).bankTransferInited();
|
||||
}
|
||||
|
||||
// 2
|
||||
public void payOfferFee(Trade trade, FutureCallback<Transaction> callback) throws InsufficientMoneyException
|
||||
{
|
||||
log.info("Pay offer fee");
|
||||
|
||||
walletFacade.payFee(Fees.OFFER_TAKER_FEE, callback);
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade process
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
log.info("Taker asks offerer for confirmation for his fee payment.");
|
||||
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
|
||||
}
|
||||
|
||||
// 3
|
||||
public void requestOffererDetailData()
|
||||
{
|
||||
log.info("Request offerer detail data");
|
||||
|
||||
}
|
||||
|
||||
// 4
|
||||
public void signContract(Contract contract)
|
||||
{
|
||||
log.info("sign Contract");
|
||||
|
||||
String contractAsJson = Utils.objectToJson(contract);
|
||||
|
||||
contract.getTrade().setJsonRepresentation(contractAsJson);
|
||||
contract.getTrade().setSignature(cryptoFacade.signContract(walletFacade.getAccountKey(), contractAsJson));
|
||||
}
|
||||
|
||||
// 5
|
||||
public void payToDepositTx(Trade trade)
|
||||
{
|
||||
//walletFacade.takerAddPaymentAndSign();
|
||||
// messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
|
||||
}
|
||||
|
||||
// 6
|
||||
public void releaseBTC(Trade trade)
|
||||
{
|
||||
|
@ -151,9 +212,13 @@ public class Trading
|
|||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public HashMap<String, Trade> getTrades()
|
||||
public Map<String, Trade> getTrades()
|
||||
{
|
||||
return trades;
|
||||
}
|
||||
|
||||
public Offer getOffer(String offerUID)
|
||||
{
|
||||
return myOffers.get(offerUID.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,565 @@
|
|||
package io.bitsquare.trade.offerer;
|
||||
|
||||
import com.google.bitcoin.core.InsufficientMoneyException;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.btc.BlockChainFacade;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.crypto.CryptoFacade;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import io.bitsquare.msg.TradeMessageType;
|
||||
import io.bitsquare.msg.listeners.TradeMessageListener;
|
||||
import io.bitsquare.trade.Contract;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
public class OffererPaymentProtocol
|
||||
{
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OffererPaymentProtocol.class);
|
||||
|
||||
private Trade trade;
|
||||
private Offer offer;
|
||||
private Contract contract;
|
||||
private OffererPaymentProtocolListener offererPaymentProtocolListener;
|
||||
private MessageFacade messageFacade;
|
||||
private WalletFacade walletFacade;
|
||||
private BlockChainFacade blockChainFacade;
|
||||
private CryptoFacade cryptoFacade;
|
||||
private User user;
|
||||
private PeerAddress peerAddress;
|
||||
private boolean isTakeOfferRequested;
|
||||
private int numberOfSteps = 20;//TODO
|
||||
private int currentStep = 0;
|
||||
private String preparedOffererDepositTxAsHex;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public OffererPaymentProtocol(Trade trade,
|
||||
OffererPaymentProtocolListener offererPaymentProtocolListener,
|
||||
MessageFacade messageFacade,
|
||||
WalletFacade walletFacade,
|
||||
BlockChainFacade blockChainFacade,
|
||||
CryptoFacade cryptoFacade,
|
||||
User user)
|
||||
{
|
||||
checkNotNull(trade);
|
||||
checkNotNull(messageFacade);
|
||||
checkNotNull(walletFacade);
|
||||
checkNotNull(blockChainFacade);
|
||||
checkNotNull(cryptoFacade);
|
||||
checkNotNull(user);
|
||||
|
||||
this.trade = trade;
|
||||
this.offererPaymentProtocolListener = offererPaymentProtocolListener;
|
||||
this.messageFacade = messageFacade;
|
||||
this.walletFacade = walletFacade;
|
||||
this.blockChainFacade = blockChainFacade;
|
||||
this.cryptoFacade = cryptoFacade;
|
||||
this.user = user;
|
||||
|
||||
offer = trade.getOffer();
|
||||
|
||||
messageFacade.addOffererPaymentProtocol(this);
|
||||
|
||||
log.debug("0 Constr");
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 1.1-1.2 Takers tasks, we start when we get the take offer request
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 1.3
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// We got a take offer request and check if the offer is not already reserved for another user.
|
||||
// If the offer is free we send an accept message.
|
||||
|
||||
public void onTakeOfferRequested(PeerAddress sender)
|
||||
{
|
||||
log.debug("1.3 onTakeOfferRequested");
|
||||
|
||||
peerAddress = sender;
|
||||
// The offer must be still available.
|
||||
// TODO check also if offer is still in our offer list, if we canceled recently there might be inconsistency
|
||||
if (!isTakeOfferRequested)
|
||||
{
|
||||
log.debug("1.3 offer not yet requested");
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onResult");
|
||||
// The accept message has arrived at the peer
|
||||
// We set requested flag and remove the offer from the orderbook
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
isTakeOfferRequested = true;
|
||||
try
|
||||
{
|
||||
messageFacade.removeOffer(offer);
|
||||
} catch (IOException e)
|
||||
{
|
||||
offererPaymentProtocolListener.onFailure("removeOffer failed " + e.getMessage());
|
||||
}
|
||||
|
||||
// It's the takers turn again, so we are in wait mode....
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.warn("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onFailed");
|
||||
offererPaymentProtocolListener.onFailure("onTakeOfferRequested onSendTradingMessageFailed");
|
||||
}
|
||||
};
|
||||
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// 1.3a Send accept take offer message
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.ACCEPT_TAKE_OFFER_REQUEST, trade.getUid());
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("1.3 offer already requested REJECT_TAKE_OFFER_REQUEST");
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REJECT_TAKE_OFFER_REQUEST, trade.getUid());
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("1.3 isTakeOfferRequested REJECT_TAKE_OFFER_REQUEST onResult");
|
||||
// no more steps are needed, as we are not interested in that trade
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.warn("1.3 isTakeOfferRequested REJECT_TAKE_OFFER_REQUEST onFailed");
|
||||
// we can ignore that as we are not interested in that trade
|
||||
}
|
||||
};
|
||||
|
||||
// Offerer reject take offer because it's not available anymore
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 1.4, 2.1 - 2.2 Takers task, we wait until the next incoming message
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.3
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onTakeOfferFeePayed(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.3 onTakeOfferFeePayed");
|
||||
trade.setTakeOfferFeeTxID(requestTradeMessage.getTakeOfferFeeTxID());
|
||||
trade.setTradeAmount(requestTradeMessage.getTradeAmount());
|
||||
verifyTakeOfferFeePayment(requestTradeMessage);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.4
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void verifyTakeOfferFeePayment(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.4 verifyTakeOfferFeePayment");
|
||||
//TODO just dummy now, will be async
|
||||
int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(requestTradeMessage.getTakeOfferFeeTxID());
|
||||
if (numOfPeersSeenTx > 2)
|
||||
{
|
||||
createDepositTx(requestTradeMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.5
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void createDepositTx(TradeMessage requestTradeMessage)
|
||||
{
|
||||
checkNotNull(requestTradeMessage);
|
||||
|
||||
log.debug("2.5 createDepositTx");
|
||||
|
||||
BigInteger collateralAmount = trade.getTradeAmount().multiply(BigInteger.valueOf(offer.getCollateral())).divide(BigInteger.valueOf(100));
|
||||
String offererPubKey = walletFacade.getMultiSigPubKeyAsHex();
|
||||
String takerPubKey = requestTradeMessage.getTakerMultiSigPubKey();
|
||||
String arbitratorPubKey = offer.getArbitrator().getPubKey();
|
||||
|
||||
checkNotNull(requestTradeMessage.getTakerMultiSigPubKey());
|
||||
checkNotNull(offererPubKey);
|
||||
checkNotNull(takerPubKey);
|
||||
checkNotNull(arbitratorPubKey);
|
||||
|
||||
log.debug("2.5 offererCreatesMSTxAndAddPayment");
|
||||
log.debug("collateralAmount " + collateralAmount);
|
||||
log.debug("offerer pubkey " + offererPubKey);
|
||||
log.debug("taker pubkey " + takerPubKey);
|
||||
log.debug("arbitrator pubkey " + arbitratorPubKey);
|
||||
try
|
||||
{
|
||||
Transaction tx = walletFacade.offererCreatesMSTxAndAddPayment(collateralAmount, offererPubKey, takerPubKey, arbitratorPubKey);
|
||||
preparedOffererDepositTxAsHex = Utils.bytesToHexString(tx.bitcoinSerialize());
|
||||
log.debug("2.5 deposit tx created: " + tx);
|
||||
log.debug("2.5 deposit txAsHex: " + preparedOffererDepositTxAsHex);
|
||||
sendDepositTxAndDataForContract(preparedOffererDepositTxAsHex, offererPubKey);
|
||||
} catch (InsufficientMoneyException e)
|
||||
{
|
||||
log.warn("2.5 InsufficientMoneyException " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.6
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void sendDepositTxAndDataForContract(String preparedOffererDepositTxAsHex, String offererPubKey)
|
||||
{
|
||||
log.debug("2.6 sendDepositTxAndDataForContract");
|
||||
// Send all the requested data
|
||||
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("2.6 sendDepositTxAndDataForContract onResult");
|
||||
// Message arrived at taker
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// We wait until we get the signed tx back
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.debug("2.6 sendDepositTxAndDataForContract onFailed");
|
||||
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
|
||||
}
|
||||
};
|
||||
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
|
||||
String accountID = user.getAccountID();
|
||||
|
||||
checkNotNull(trade.getUid());
|
||||
checkNotNull(bankAccount);
|
||||
checkNotNull(accountID);
|
||||
checkNotNull(preparedOffererDepositTxAsHex);
|
||||
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getUid(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex);
|
||||
log.debug("2.6 sendTradingMessage");
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
|
||||
//************************************************************************************************
|
||||
// 2.7 - 2.11 Takers task, we wait until the next incoming message
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.1 Incoming msg from taker
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onDepositTxReadyForPublication(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("3.1 onDepositTxReadyForPublication");
|
||||
verifyTaker(requestTradeMessage);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.2 Verify offerers account registration and against the blacklist
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void verifyTaker(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("3.2 verifyTaker");
|
||||
log.debug("3.2.1 verifyAccountRegistration");
|
||||
if (blockChainFacade.verifyAccountRegistration())
|
||||
{
|
||||
log.debug("3.2.2 isAccountBlackListed");
|
||||
if (!blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount()))
|
||||
{
|
||||
verifyAndSignContract(requestTradeMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
offererPaymentProtocolListener.onFailure("Taker is blacklisted.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
offererPaymentProtocolListener.onFailure("Takers account registration is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.3 Verify and sign the contract
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void verifyAndSignContract(TradeMessage requestTradeMessage)
|
||||
{
|
||||
contract = new Contract(offer,
|
||||
trade.getTradeAmount(),
|
||||
trade.getTakeOfferFeeTxID(),
|
||||
user.getAccountID(),
|
||||
requestTradeMessage.getAccountID(),
|
||||
user.getCurrentBankAccount(),
|
||||
requestTradeMessage.getBankAccount(),
|
||||
offer.getMessagePubKeyAsHex(),
|
||||
requestTradeMessage.getTakerMessagePubKey());
|
||||
|
||||
log.debug("3.3 offerer contract created: " + contract.toString());
|
||||
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
log.debug("3.3 contractAsJson: " + contractAsJson);
|
||||
log.debug("3.3 requestTradingMessage.getContractAsJson(): " + requestTradeMessage.getContractAsJson());
|
||||
|
||||
// TODO generic json creates too complex object, at least the PublicKeys need to be removed
|
||||
boolean isEqual = contractAsJson.equals(requestTradeMessage.getContractAsJson());
|
||||
log.debug("3.3 does json match?: " + isEqual);
|
||||
|
||||
/* if (contractAsJson.equals(requestTradingMessage.getContractAsJson()))
|
||||
{ */
|
||||
String signature = cryptoFacade.signContract(walletFacade.getAccountRegistrationKey(), contractAsJson);
|
||||
trade.setContract(contract);
|
||||
trade.setContractAsJson(contractAsJson);
|
||||
trade.setContractTakerSignature(signature);
|
||||
log.debug("3.3 signature: " + signature);
|
||||
|
||||
signAndPublishDepositTx(requestTradeMessage);
|
||||
/* }
|
||||
else
|
||||
{
|
||||
log.error("3.3 verifyContract failed");
|
||||
} */
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.4 Sign and publish the deposit tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void signAndPublishDepositTx(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("3.4 signAndPublishDepositTx");
|
||||
|
||||
String signedTakerDepositTxAsHex = requestTradeMessage.getSignedTakerDepositTxAsHex();
|
||||
String txConnOutAsHex = requestTradeMessage.getTxConnOutAsHex();
|
||||
String txScriptSigAsHex = requestTradeMessage.getTxScriptSigAsHex();
|
||||
|
||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
{
|
||||
log.info("3.4 signAndPublishDepositTx offererSignAndSendTx onSuccess:" + transaction.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.error("3.4 signAndPublishDepositTx offererSignAndSendTx onFailure:" + t.getMessage());
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
log.debug("3.4 offererSignAndSendTx");
|
||||
Transaction transaction = walletFacade.offererSignAndSendTx(preparedOffererDepositTxAsHex, signedTakerDepositTxAsHex, txConnOutAsHex, txScriptSigAsHex, callback);
|
||||
String txID = transaction.getHashAsString();
|
||||
trade.setDepositTransaction(transaction);
|
||||
log.debug("3.4 deposit tx published: " + transaction);
|
||||
log.debug("3.4 deposit txID: " + txID);
|
||||
|
||||
sendDepositTxIdToTaker(transaction);
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error("3.4 error at walletFacade.offererSignAndSendTx: " + e.getMessage());
|
||||
e.getStackTrace();// Could not understand form of connected output script: RETURN
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.5 Send tx id of published deposit tx to taker
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void sendDepositTxIdToTaker(Transaction transaction)
|
||||
{
|
||||
log.debug("3.5 sendDepositTxIdToTaker");
|
||||
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onResult");
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.warn("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onFailed");
|
||||
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
|
||||
}
|
||||
};
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getUid(), transaction.getHashAsString());
|
||||
log.debug("3.5 sendTradingMessage");
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
|
||||
// wait for at least 1 confirmation, then pay Fiat
|
||||
offererPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID());
|
||||
setupListenerForBlockChainConfirmation(transaction);
|
||||
}
|
||||
|
||||
//************************************************************************************************
|
||||
// 3.6 Taker got informed, but no action from his side required.
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.7 We setup a listener for block chain confirmation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setupListenerForBlockChainConfirmation(Transaction transaction)
|
||||
{
|
||||
log.debug("3.7 setupListenerForBlockChainConfirmation");
|
||||
|
||||
// for testing
|
||||
// TODO listeners mut be done with blockchain not wallet
|
||||
onDepositTxConfirmedInBlockchain();
|
||||
|
||||
transaction.getConfidence().addEventListener(new TransactionConfidence.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onConfidenceChanged(Transaction tx, ChangeReason reason)
|
||||
{
|
||||
log.info("onConfidenceChanged reason = " + reason);
|
||||
log.info("onConfidenceChanged confidence = " + tx.getConfidence().toString());
|
||||
if (reason == ChangeReason.SEEN_PEERS)
|
||||
{
|
||||
updateConfirmation(tx.getConfidence());
|
||||
|
||||
log.debug("### confidence.numBroadcastPeers() " + tx.getConfidence().numBroadcastPeers());
|
||||
|
||||
//todo just for testing now, dont like to wait so long...
|
||||
if (tx.getConfidence().numBroadcastPeers() > 3)
|
||||
{
|
||||
onDepositTxConfirmedInBlockchain();
|
||||
transaction.getConfidence().removeEventListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
||||
{
|
||||
onDepositTxConfirmedInBlockchain();
|
||||
transaction.getConfidence().removeEventListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.8 We check if the block chain confirmation is >= 1
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateConfirmation(TransactionConfidence confidence)
|
||||
{
|
||||
log.debug("3.8 updateConfirmation " + confidence.toString());
|
||||
offererPaymentProtocolListener.onDepositTxConfirmedUpdate(confidence);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.9 Blockchain confirmation received, so tell user he should start bank transfer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void onDepositTxConfirmedInBlockchain()
|
||||
{
|
||||
log.debug("3.9 readyForBankTransfer");
|
||||
offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
|
||||
}
|
||||
|
||||
//************************************************************************************************
|
||||
// Offerer need to start bank tx, after he done it he call the next step
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.10 User clicked the "bank transfer inited" button, so we tell the peer that we started the bank tx
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void bankTransferInited()
|
||||
{
|
||||
log.debug("3.10 bankTransferInited");
|
||||
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("3.10 bankTransferInited BANK_TX_INITED onResult");
|
||||
log.debug("########## LAST STEP OFFERER FOR FIRST PART");
|
||||
|
||||
offererPaymentProtocolListener.onProgress(getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.warn("3.10 bankTransferInited BANK_TX_INITED onFailed");
|
||||
offererPaymentProtocolListener.onFailure("bankTransferInited BANK_TX_INITED");
|
||||
}
|
||||
};
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getUid());
|
||||
log.debug("3.10 sendTradingMessage BANK_TX_INITED");
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// We wait until taker has received the money on his bank account, that might take a while.
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
private double getProgress()
|
||||
{
|
||||
currentStep++;
|
||||
return (double) currentStep / (double) numberOfSteps;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.bitsquare.trade.offerer;
|
||||
|
||||
import com.google.bitcoin.core.TransactionConfidence;
|
||||
|
||||
public interface OffererPaymentProtocolListener
|
||||
{
|
||||
void onProgress(double progress);
|
||||
|
||||
void onFailure(String failureMessage);
|
||||
|
||||
void onDepositTxPublished(String depositTxID);
|
||||
|
||||
void onDepositTxConfirmedInBlockchain();
|
||||
|
||||
void onDepositTxConfirmedUpdate(TransactionConfidence confidence);
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package io.bitsquare.trade.orderbook;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.btc.BtcFormatter;
|
||||
import io.bitsquare.gui.market.orderbook.OrderBookListItem;
|
||||
import io.bitsquare.gui.util.Converter;
|
||||
import io.bitsquare.gui.util.Formatter;
|
||||
import io.bitsquare.settings.Settings;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.MockData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MockOrderBook extends OrderBook
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MockOrderBook.class);
|
||||
|
||||
@Inject
|
||||
public MockOrderBook(Settings settings, User user)
|
||||
{
|
||||
super(settings, user);
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
allOffers.add(new OrderBookListItem(getOffer()));
|
||||
}
|
||||
}
|
||||
|
||||
private Offer getOffer()
|
||||
{
|
||||
double amount = Math.random() * 10 + 0.1;
|
||||
amount = Converter.stringToDouble(Formatter.formatAmount(amount));
|
||||
double minAmount = Math.random() * amount;
|
||||
minAmount = Converter.stringToDouble(Formatter.formatAmount(minAmount));
|
||||
|
||||
Direction direction = Direction.BUY;
|
||||
double price = 500 + Math.random() * 50;
|
||||
if (Math.random() > 0.5)
|
||||
{
|
||||
direction = Direction.SELL;
|
||||
price = 500 - Math.random() * 50;
|
||||
}
|
||||
double collateral = 0.1;// Math.random() * 20 + 0.1;
|
||||
Offer offer = new Offer("mjbxLbuVpU1cNXLJbrJZyirYwweoRPVVTj",
|
||||
UUID.randomUUID().toString(),
|
||||
direction,
|
||||
price,
|
||||
BtcFormatter.doubleValueToSatoshis(amount),
|
||||
BtcFormatter.doubleValueToSatoshis(minAmount),
|
||||
MockData.getRandomBankTransferTypeEnums().get(0),
|
||||
MockData.getRandomCurrencies().get(0),
|
||||
MockData.getRandomLocales().get(0),
|
||||
MockData.getRandomArbitrators().get(0),
|
||||
collateral,
|
||||
MockData.getRandomLocales(),
|
||||
MockData.getRandomLocales());
|
||||
|
||||
return offer;
|
||||
}
|
||||
}
|
|
@ -3,28 +3,37 @@ package io.bitsquare.trade.orderbook;
|
|||
import com.google.inject.Inject;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.gui.market.orderbook.OrderBookListItem;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.listeners.OrderBookListener;
|
||||
import io.bitsquare.settings.Settings;
|
||||
import io.bitsquare.trade.Direction;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trading;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
import io.bitsquare.user.User;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.storage.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class OrderBook
|
||||
public class OrderBook implements OrderBookListener
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(OrderBook.class);
|
||||
|
||||
private Settings settings;
|
||||
private User user;
|
||||
private MessageFacade messageFacade;
|
||||
private Trading trading;
|
||||
|
||||
protected ObservableList<OrderBookListItem> allOffers = FXCollections.observableArrayList();
|
||||
private FilteredList<OrderBookListItem> filteredList = new FilteredList<>(allOffers);
|
||||
|
@ -37,10 +46,12 @@ public class OrderBook
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public OrderBook(Settings settings, User user)
|
||||
public OrderBook(Settings settings, User user, MessageFacade messageFacade, Trading trading)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.user = user;
|
||||
this.messageFacade = messageFacade;
|
||||
this.trading = trading;
|
||||
}
|
||||
|
||||
|
||||
|
@ -48,7 +59,29 @@ public class OrderBook
|
|||
// Public API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void updateFilter(OrderBookFilter orderBookFilter)
|
||||
|
||||
public void init()
|
||||
{
|
||||
messageFacade.addMessageListener(this);
|
||||
}
|
||||
|
||||
public void cleanup()
|
||||
{
|
||||
messageFacade.removeMessageListener(this);
|
||||
}
|
||||
|
||||
public void loadOffers()
|
||||
{
|
||||
messageFacade.getOffers(user.getCurrentBankAccount().getCurrency().getCurrencyCode());
|
||||
}
|
||||
|
||||
public void removeOffer(Offer offer) throws IOException
|
||||
{
|
||||
trading.removeOffer(offer);
|
||||
messageFacade.removeOffer(offer);
|
||||
}
|
||||
|
||||
public void applyFilter(OrderBookFilter orderBookFilter)
|
||||
{
|
||||
filteredList.setPredicate(new Predicate<OrderBookListItem>()
|
||||
{
|
||||
|
@ -73,7 +106,7 @@ public class OrderBook
|
|||
// One of the supported languages from the settings must match one of the offer languages (n to n)
|
||||
boolean languageResult = languagesInList(settings.getAcceptedLanguageLocales(), offer.getAcceptedLanguageLocales());
|
||||
|
||||
// Apply updateFilter only if there is a valid value set
|
||||
// Apply applyFilter only if there is a valid value set
|
||||
// The requested amount must be lower or equal then the offer amount
|
||||
boolean amountResult = true;
|
||||
if (orderBookFilter.getAmount() > 0)
|
||||
|
@ -82,7 +115,7 @@ public class OrderBook
|
|||
// The requested trade direction must be opposite of the offerList trade direction
|
||||
boolean directionResult = !orderBookFilter.getDirection().equals(offer.getDirection());
|
||||
|
||||
// Apply updateFilter only if there is a valid value set
|
||||
// Apply applyFilter only if there is a valid value set
|
||||
boolean priceResult = true;
|
||||
if (orderBookFilter.getPrice() > 0)
|
||||
{
|
||||
|
@ -104,7 +137,7 @@ public class OrderBook
|
|||
&& priceResult
|
||||
&& arbitratorResult;
|
||||
|
||||
/*
|
||||
/*
|
||||
log.debug("result = " + result +
|
||||
", currencyResult = " + currencyResult +
|
||||
", countryResult = " + countryResult +
|
||||
|
@ -131,12 +164,93 @@ public class OrderBook
|
|||
", offer.getPrice() = " + offer.getPrice());
|
||||
log.debug("offer.getArbitrator() = " + offer.getArbitrator() +
|
||||
", settings.getAcceptedArbitrators() = " + settings.getAcceptedArbitrators());
|
||||
*/
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Interface implementation: MessageListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onOfferAdded(Data offerData, boolean success)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
if (offerDataObject instanceof Offer && offerDataObject != null)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
allOffers.add(new OrderBookListItem(offer));
|
||||
}
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
{
|
||||
if (success && dataMap != null)
|
||||
{
|
||||
allOffers.clear();
|
||||
for (Data offerData : dataMap.values())
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
if (offerDataObject instanceof Offer && offerDataObject != null)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
OrderBookListItem orderBookListItem = new OrderBookListItem(offer);
|
||||
allOffers.add(orderBookListItem);
|
||||
}
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
allOffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOfferRemoved(Data offerData, boolean success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
if (offerDataObject instanceof Offer && offerDataObject != null)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
allOffers.removeIf(new Predicate<OrderBookListItem>()
|
||||
{
|
||||
@Override
|
||||
public boolean test(OrderBookListItem orderBookListItem)
|
||||
{
|
||||
return orderBookListItem.getOffer().getUid().equals(offer.getUid());
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("onOfferRemoved failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -147,6 +261,7 @@ public class OrderBook
|
|||
return offerList;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -182,7 +297,7 @@ public class OrderBook
|
|||
{
|
||||
try
|
||||
{
|
||||
if (arbitrator.getUID().equals(arbitratorToMatch.getUID()))
|
||||
if (arbitrator.getUid().equals(arbitratorToMatch.getUid()))
|
||||
return true;
|
||||
} catch (Exception e)
|
||||
{
|
||||
|
@ -192,4 +307,6 @@ public class OrderBook
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||
|
||||
public class OrderBookFilter
|
||||
{
|
||||
transient private final SimpleBooleanProperty changedProperty = new SimpleBooleanProperty();
|
||||
transient private final SimpleBooleanProperty directionChangedProperty = new SimpleBooleanProperty();
|
||||
|
||||
private double price;
|
||||
private double amount;
|
||||
|
@ -19,19 +19,17 @@ public class OrderBookFilter
|
|||
public void setAmount(double amount)
|
||||
{
|
||||
this.amount = amount;
|
||||
triggerChange();
|
||||
}
|
||||
|
||||
public void setPrice(double price)
|
||||
{
|
||||
this.price = price;
|
||||
triggerChange();
|
||||
}
|
||||
|
||||
public void setDirection(Direction direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
triggerChange();
|
||||
directionChangedProperty.set(!directionChangedProperty.get());
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,19 +52,10 @@ public class OrderBookFilter
|
|||
return price;
|
||||
}
|
||||
|
||||
public SimpleBooleanProperty getChangedProperty()
|
||||
public SimpleBooleanProperty getDirectionChangedProperty()
|
||||
{
|
||||
return changedProperty;
|
||||
return directionChangedProperty;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void triggerChange()
|
||||
{
|
||||
changedProperty.set(!changedProperty.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
501
src/main/java/io/bitsquare/trade/taker/TakerPaymentProtocol.java
Normal file
|
@ -0,0 +1,501 @@
|
|||
package io.bitsquare.trade.taker;
|
||||
|
||||
import com.google.bitcoin.core.AddressFormatException;
|
||||
import com.google.bitcoin.core.InsufficientMoneyException;
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.btc.BlockChainFacade;
|
||||
import io.bitsquare.btc.Fees;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.crypto.CryptoFacade;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import io.bitsquare.msg.TradeMessageType;
|
||||
import io.bitsquare.msg.listeners.AddressLookupListener;
|
||||
import io.bitsquare.msg.listeners.TradeMessageListener;
|
||||
import io.bitsquare.trade.Contract;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
public class TakerPaymentProtocol
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class);
|
||||
|
||||
private Trade trade;
|
||||
private Offer offer;
|
||||
private Contract contract;
|
||||
private TakerPaymentProtocolListener takerPaymentProtocolListener;
|
||||
private MessageFacade messageFacade;
|
||||
private WalletFacade walletFacade;
|
||||
private BlockChainFacade blockChainFacade;
|
||||
private CryptoFacade cryptoFacade;
|
||||
private User user;
|
||||
private PeerAddress peerAddress;
|
||||
private boolean isTakeOfferRequested;
|
||||
private int numberOfSteps = 6;//TODO
|
||||
private int currentStep = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public TakerPaymentProtocol(Trade trade,
|
||||
TakerPaymentProtocolListener takerPaymentProtocolListener,
|
||||
MessageFacade messageFacade,
|
||||
WalletFacade walletFacade,
|
||||
BlockChainFacade blockChainFacade,
|
||||
CryptoFacade cryptoFacade,
|
||||
User user)
|
||||
{
|
||||
this.trade = trade;
|
||||
this.takerPaymentProtocolListener = takerPaymentProtocolListener;
|
||||
this.messageFacade = messageFacade;
|
||||
this.walletFacade = walletFacade;
|
||||
this.blockChainFacade = blockChainFacade;
|
||||
this.cryptoFacade = cryptoFacade;
|
||||
this.user = user;
|
||||
|
||||
offer = trade.getOffer();
|
||||
|
||||
messageFacade.addTakerPaymentProtocol(this);
|
||||
}
|
||||
|
||||
public void takeOffer()
|
||||
{
|
||||
log.debug("1 takeOffer");
|
||||
findPeerAddress();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 1.1
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void findPeerAddress()
|
||||
{
|
||||
log.debug("1.1 findPeerAddress");
|
||||
AddressLookupListener addressLookupListener = new AddressLookupListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult(PeerAddress address)
|
||||
{
|
||||
log.debug("1.1 findPeerAddress onResult");
|
||||
// We got the peer address
|
||||
peerAddress = address;
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// next
|
||||
requestTakeOffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.debug("1.1 findPeerAddress onFailed");
|
||||
takerPaymentProtocolListener.onFailure("onGetPeerAddressFailed");
|
||||
}
|
||||
};
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// Request the peers address from the DHT
|
||||
messageFacade.getPeerAddress(offer.getMessagePubKeyAsHex(), addressLookupListener);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 1.2
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void requestTakeOffer()
|
||||
{
|
||||
log.debug("1.2 requestTakeOffer");
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("1.2 requestTakeOffer onResult");
|
||||
// Our message has arrived
|
||||
// We don't know yet if the offerer has accepted
|
||||
// the take request (if offer is not already reserved for another user)
|
||||
// We await for an incoming message from the offerer with the accept msg
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// Wait for message from offerer...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.debug("1.2 requestTakeOffer onFailed");
|
||||
takerPaymentProtocolListener.onFailure("sendTakeOfferRequest onSendTradingMessageFailed");
|
||||
}
|
||||
};
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// Send the take offer request
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKE_OFFER, trade.getUid());
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 1.3. Offerers tasks, we are in waiting mode
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 1.4
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 1.4a offerer has accepted the take offer request. Move on to step 2.
|
||||
public void onTakeOfferRequestAccepted()
|
||||
{
|
||||
log.debug("1.4a onTakeOfferRequestAccepted");
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
payOfferFee(trade);
|
||||
}
|
||||
|
||||
// 1.4b Offerer has rejected the take offer request. The UI controller will handle the case.
|
||||
public void onTakeOfferRequestRejected()
|
||||
{
|
||||
log.debug("1.4b onTakeOfferRequestRejected");
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.1
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void payOfferFee(Trade trade)
|
||||
{
|
||||
log.debug("2.1 payOfferFee");
|
||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction)
|
||||
{
|
||||
log.debug("2.1 payOfferFee onSuccess");
|
||||
log.info("sendResult onSuccess txid:" + transaction.getHashAsString());
|
||||
|
||||
// Offer fee payed successfully.
|
||||
trade.setTakeOfferFeeTxID(transaction.getHashAsString());
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// move on
|
||||
sendTakerOfferFeeTxID(transaction.getHashAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.debug("2.1 payOfferFee onFailure");
|
||||
takerPaymentProtocolListener.onFailure("payOfferFee onFailure " + t.getMessage());
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
// Pay the offer fee
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
walletFacade.payFee(Fees.OFFER_TAKER_FEE, callback);
|
||||
} catch (InsufficientMoneyException e)
|
||||
{
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.2
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Request peers account details. At that moment private data (bank account nr.) of the offerer get visible to the taker.
|
||||
// We send also the tx id of the fee payment, so the offerer can confirm that the payment is seen in the network
|
||||
// 0 block confirmation is acceptable for the fee to not block the process
|
||||
// The offerer will wait until a minimum of peers seen the tx before sending his data.
|
||||
// We also get the multisig tx delivered
|
||||
private void sendTakerOfferFeeTxID(String takeOfferFeeTxID)
|
||||
{
|
||||
log.debug("2.2 sendTakerOfferFeeTxID");
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("2.2 sendTakerOfferFeeTxID onResult");
|
||||
// Message has arrived
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// We wait until the offerer send us the data
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.debug("2.2 sendTakerOfferFeeTxID onFailed");
|
||||
takerPaymentProtocolListener.onFailure("requestAccountDetails onSendTradingMessageFailed");
|
||||
}
|
||||
};
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
// 2.3. send request for the account details and send fee tx id so offerer can verify that the fee has been paid.
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.TAKE_OFFER_FEE_PAYED,
|
||||
trade.getUid(),
|
||||
trade.getTradeAmount(),
|
||||
takeOfferFeeTxID,
|
||||
walletFacade.getMultiSigPubKeyAsHex());
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 2.3 - 2.6 Offerers tasks, we are in waiting mode
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.7 Incoming msg from offerer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onTakerDepositPaymentRequested(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.7 onTakerDepositPaymentRequested");
|
||||
verifyOfferer(requestTradeMessage);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.8 Verify offerers account registration and against the blacklist
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void verifyOfferer(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.8 verifyOfferer");
|
||||
log.debug("2.8.1 verifyAccountRegistration");
|
||||
if (blockChainFacade.verifyAccountRegistration())
|
||||
{
|
||||
log.debug("2.8.2 isAccountBlackListed");
|
||||
if (!blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount()))
|
||||
{
|
||||
createAndSignContract(requestTradeMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
takerPaymentProtocolListener.onFailure("Offerer is blacklisted.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
takerPaymentProtocolListener.onFailure("Offerers account registration is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.9 Create and sign the contract
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void createAndSignContract(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.9 createAndSignContract");
|
||||
checkNotNull(offer);
|
||||
checkNotNull(trade.getTradeAmount());
|
||||
checkNotNull(trade.getTakeOfferFeeTxID());
|
||||
checkNotNull(requestTradeMessage.getAccountID());
|
||||
checkNotNull(user.getAccountID());
|
||||
checkNotNull(requestTradeMessage.getBankAccount());
|
||||
checkNotNull(user.getCurrentBankAccount());
|
||||
checkNotNull(user.getMessagePubKeyAsHex());
|
||||
|
||||
contract = new Contract(offer,
|
||||
trade.getTradeAmount(),
|
||||
trade.getTakeOfferFeeTxID(),
|
||||
requestTradeMessage.getAccountID(),
|
||||
user.getAccountID(),
|
||||
requestTradeMessage.getBankAccount(),
|
||||
user.getCurrentBankAccount(),
|
||||
offer.getMessagePubKeyAsHex(),
|
||||
user.getMessagePubKeyAsHex()
|
||||
);
|
||||
|
||||
log.debug("2.9 contract created: " + contract.toString());
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
String signature = cryptoFacade.signContract(walletFacade.getAccountRegistrationKey(), contractAsJson);
|
||||
|
||||
//log.debug("2.9 contractAsJson: " + contractAsJson);
|
||||
log.debug("2.9 contract signature: " + signature);
|
||||
|
||||
trade.setContract(contract);
|
||||
trade.setContractAsJson(contractAsJson);
|
||||
trade.setContractTakerSignature(signature);
|
||||
|
||||
payDeposit(requestTradeMessage);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.10 Pay in the funds to the deposit tx and sign it
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void payDeposit(TradeMessage requestTradeMessage)
|
||||
{
|
||||
log.debug("2.10 payDeposit");
|
||||
|
||||
BigInteger collateralAmount = trade.getTradeAmount().multiply(BigInteger.valueOf(offer.getCollateral())).divide(BigInteger.valueOf(100));
|
||||
BigInteger takerAmount = trade.getTradeAmount().add(collateralAmount);
|
||||
BigInteger msOutputAmount = trade.getTradeAmount().add(collateralAmount).add(collateralAmount);
|
||||
String offererPubKey = requestTradeMessage.getOffererPubKey();
|
||||
String takerPubKey = walletFacade.getMultiSigPubKeyAsHex();
|
||||
String arbitratorPubKey = offer.getArbitrator().getPubKey();
|
||||
String preparedOffererDepositTxAsHex = requestTradeMessage.getPreparedOffererDepositTxAsHex();
|
||||
|
||||
checkNotNull(takerAmount);
|
||||
checkNotNull(msOutputAmount);
|
||||
checkNotNull(offererPubKey);
|
||||
checkNotNull(takerPubKey);
|
||||
checkNotNull(arbitratorPubKey);
|
||||
checkNotNull(preparedOffererDepositTxAsHex);
|
||||
|
||||
log.debug("2.10 offererCreatesMSTxAndAddPayment");
|
||||
log.debug("takerAmount " + takerAmount);
|
||||
log.debug("msOutputAmount " + msOutputAmount);
|
||||
log.debug("offerer pubkey " + offererPubKey);
|
||||
log.debug("taker pubkey " + takerPubKey);
|
||||
log.debug("arbitrator pubkey " + arbitratorPubKey);
|
||||
log.debug("preparedOffererDepositTxAsHex " + preparedOffererDepositTxAsHex);
|
||||
try
|
||||
{
|
||||
Transaction signedTakerDepositTx = walletFacade.takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, preparedOffererDepositTxAsHex);
|
||||
log.debug("2.10 deposit tx created: " + signedTakerDepositTx);
|
||||
sendSignedTakerDepositTxAsHex(signedTakerDepositTx);
|
||||
} catch (InterruptedException | AddressFormatException | ExecutionException | InsufficientMoneyException e)
|
||||
{
|
||||
log.error("2.10 error at walletFacade.takerAddPaymentAndSign: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 2.11 Send the tx to the offerer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void sendSignedTakerDepositTxAsHex(Transaction signedTakerDepositTx)
|
||||
{
|
||||
log.debug("2.11 sendSignedTakerDepositTxAsHex");
|
||||
|
||||
TradeMessageListener listener = new TradeMessageListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult()
|
||||
{
|
||||
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onResult");
|
||||
// Message arrived at taker
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed()
|
||||
{
|
||||
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
|
||||
takerPaymentProtocolListener.onFailure("sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
|
||||
}
|
||||
};
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
|
||||
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
|
||||
String accountID = user.getAccountID();
|
||||
String messagePubKey = user.getMessagePubKeyAsHex();
|
||||
String contractAsJson = trade.getContractAsJson();
|
||||
String signature = trade.getTakerSignature();
|
||||
|
||||
String signedTakerDepositTxAsHex = com.google.bitcoin.core.Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize());
|
||||
String txScriptSigAsHex = com.google.bitcoin.core.Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes());
|
||||
String txConnOutAsHex = com.google.bitcoin.core.Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
|
||||
log.debug("2.10 deposit txAsHex: " + signedTakerDepositTxAsHex);
|
||||
log.debug("2.10 txScriptSigAsHex: " + txScriptSigAsHex);
|
||||
log.debug("2.10 txConnOutAsHex: " + txConnOutAsHex);
|
||||
|
||||
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_OFFERER_DEPOSIT_PUBLICATION,
|
||||
trade.getUid(),
|
||||
bankAccount,
|
||||
accountID,
|
||||
messagePubKey,
|
||||
signedTakerDepositTxAsHex,
|
||||
txScriptSigAsHex,
|
||||
txConnOutAsHex,
|
||||
contractAsJson,
|
||||
signature);
|
||||
|
||||
log.debug("2.11 sendTradingMessage");
|
||||
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 3.1 - 3.5 Offerers tasks, we are in waiting mode
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.6 Incoming msg from offerer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onDepositTxPublished(TradeMessage tradeMessage)
|
||||
{
|
||||
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxID());
|
||||
|
||||
takerPaymentProtocolListener.onProgress(getProgress());
|
||||
takerPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID());
|
||||
}
|
||||
|
||||
|
||||
//************************************************************************************************
|
||||
// 3.7-3.10 Offerers tasks, we are in waiting mode
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Step 3.11 Incoming msg from offerer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onBankTransferInited()
|
||||
{
|
||||
log.debug("3.11 Bank transfer inited msg received");
|
||||
log.debug("########## LAST STEP TAKER FOR FIRST PART");
|
||||
|
||||
takerPaymentProtocolListener.onBankTransferInited();
|
||||
}
|
||||
|
||||
//************************************************************************************************
|
||||
// Taker will check periodically his bank account until he received the money. That might take a while...
|
||||
//************************************************************************************************
|
||||
|
||||
|
||||
private double getProgress()
|
||||
{
|
||||
currentStep++;
|
||||
return (double) currentStep / (double) numberOfSteps;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.bitsquare.trade.taker;
|
||||
|
||||
public interface TakerPaymentProtocolListener
|
||||
{
|
||||
void onProgress(double progress);
|
||||
|
||||
void onFailure(String failureMessage);
|
||||
|
||||
void onDepositTxPublished(String depositTxID);
|
||||
|
||||
void onBankTransferInited();
|
||||
}
|
|
@ -9,25 +9,23 @@ public class Arbitrator implements Serializable
|
|||
|
||||
private String name;
|
||||
private String pubKey;
|
||||
private String messageID;
|
||||
private String messagePubKeyAsHex;
|
||||
private String url;
|
||||
|
||||
|
||||
private double baseFeePercent;
|
||||
private double arbitrationFeePercent;
|
||||
private BigInteger minArbitrationFee;
|
||||
private int baseFeePercent; // in percent int 1 for 1%
|
||||
private int arbitrationFeePercent;
|
||||
private BigInteger minArbitrationAmount; // in Satoshis
|
||||
private String uid;
|
||||
|
||||
public Arbitrator(String uid, String name, String pubKey, String messageID, String url, double baseFeePercent, double arbitrationFeePercent, BigInteger minArbitrationFee)
|
||||
public Arbitrator(String uid, String name, String pubKey, String messagePubKeyAsHex, String url, int baseFeePercent, int arbitrationFeePercent, BigInteger minArbitrationAmount)
|
||||
{
|
||||
this.uid = uid;
|
||||
this.name = name;
|
||||
this.pubKey = pubKey;
|
||||
this.messageID = messageID;
|
||||
this.messagePubKeyAsHex = messagePubKeyAsHex;
|
||||
this.url = url;
|
||||
this.baseFeePercent = baseFeePercent;
|
||||
this.arbitrationFeePercent = arbitrationFeePercent;
|
||||
this.minArbitrationFee = minArbitrationFee;
|
||||
this.minArbitrationAmount = minArbitrationAmount;
|
||||
}
|
||||
|
||||
|
||||
|
@ -45,9 +43,9 @@ public class Arbitrator implements Serializable
|
|||
return pubKey;
|
||||
}
|
||||
|
||||
public String getMessageID()
|
||||
public String getMessagePubKeyAsHex()
|
||||
{
|
||||
return messageID;
|
||||
return messagePubKeyAsHex;
|
||||
}
|
||||
|
||||
public String getUrl()
|
||||
|
@ -55,23 +53,38 @@ public class Arbitrator implements Serializable
|
|||
return url;
|
||||
}
|
||||
|
||||
public BigInteger getMinArbitrationFee()
|
||||
public BigInteger getMinArbitrationAmount()
|
||||
{
|
||||
return minArbitrationFee;
|
||||
return minArbitrationAmount;
|
||||
}
|
||||
|
||||
public double getBaseFeePercent()
|
||||
public int getBaseFeePercent()
|
||||
{
|
||||
return baseFeePercent;
|
||||
}
|
||||
|
||||
public double getArbitrationFeePercent()
|
||||
public int getArbitrationFeePercent()
|
||||
{
|
||||
return arbitrationFeePercent;
|
||||
}
|
||||
|
||||
public Object getUID()
|
||||
public Object getUid()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Arbitrator{" +
|
||||
"name='" + name + '\'' +
|
||||
", pubKey='" + pubKey + '\'' +
|
||||
", messagePubKeyAsHex='" + messagePubKeyAsHex + '\'' +
|
||||
", url='" + url + '\'' +
|
||||
", baseFeePercent=" + baseFeePercent +
|
||||
", arbitrationFeePercent=" + arbitrationFeePercent +
|
||||
", minArbitrationAmount=" + minArbitrationAmount +
|
||||
", uid='" + uid + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,15 +12,14 @@ public class User implements Serializable
|
|||
{
|
||||
private static final long serialVersionUID = 7409078808248518638L;
|
||||
|
||||
transient private final SimpleBooleanProperty changedProperty = new SimpleBooleanProperty();
|
||||
transient private final SimpleBooleanProperty bankAccountChangedProperty = new SimpleBooleanProperty();
|
||||
|
||||
private String accountID;
|
||||
private String messageID;
|
||||
private String messagePubKeyAsHex;
|
||||
private boolean isOnline;
|
||||
private List<BankAccount> bankAccounts = new ArrayList<>();
|
||||
private BankAccount currentBankAccount = null;
|
||||
|
||||
|
||||
public User()
|
||||
{
|
||||
}
|
||||
|
@ -35,7 +34,7 @@ public class User implements Serializable
|
|||
if (savedUser != null)
|
||||
{
|
||||
accountID = savedUser.getAccountID();
|
||||
messageID = savedUser.getMessageID();
|
||||
messagePubKeyAsHex = savedUser.getMessagePubKeyAsHex();
|
||||
isOnline = savedUser.getIsOnline();
|
||||
bankAccounts = savedUser.getBankAccounts();
|
||||
currentBankAccount = savedUser.getCurrentBankAccount();
|
||||
|
@ -53,9 +52,9 @@ public class User implements Serializable
|
|||
// Setters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setMessageID(String messageID)
|
||||
public void setMessagePubKeyAsHex(String messageID)
|
||||
{
|
||||
this.messageID = messageID;
|
||||
this.messagePubKeyAsHex = messageID;
|
||||
}
|
||||
|
||||
public void setAccountID(String accountID)
|
||||
|
@ -71,7 +70,7 @@ public class User implements Serializable
|
|||
public void setCurrentBankAccount(BankAccount currentBankAccount)
|
||||
{
|
||||
this.currentBankAccount = currentBankAccount;
|
||||
triggerChange();
|
||||
bankAccountChangedProperty.set(!bankAccountChangedProperty.get());
|
||||
}
|
||||
|
||||
public void setIsOnline(boolean isOnline)
|
||||
|
@ -90,7 +89,7 @@ public class User implements Serializable
|
|||
for (Iterator<BankAccount> iterator = getBankAccounts().iterator(); iterator.hasNext(); )
|
||||
{
|
||||
BankAccount bankAccount = iterator.next();
|
||||
bankAccountUIDs += bankAccount.getStringifiedBankAccount();
|
||||
bankAccountUIDs += bankAccount.toString();
|
||||
|
||||
if (iterator.hasNext())
|
||||
bankAccountUIDs += ", ";
|
||||
|
@ -98,9 +97,9 @@ public class User implements Serializable
|
|||
return bankAccountUIDs;
|
||||
}
|
||||
|
||||
public String getMessageID()
|
||||
public String getMessagePubKeyAsHex()
|
||||
{
|
||||
return messageID;
|
||||
return messagePubKeyAsHex;
|
||||
}
|
||||
|
||||
public String getAccountID()
|
||||
|
@ -118,23 +117,37 @@ public class User implements Serializable
|
|||
return currentBankAccount;
|
||||
}
|
||||
|
||||
public BankAccount getBankAccount(String bankAccountUID)
|
||||
{
|
||||
for (Iterator<BankAccount> iterator = bankAccounts.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
BankAccount bankAccount = iterator.next();
|
||||
if (bankAccount.getUid().equals(bankAccountUID))
|
||||
return bankAccount;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getIsOnline()
|
||||
{
|
||||
return isOnline;
|
||||
}
|
||||
|
||||
public SimpleBooleanProperty getChangedProperty()
|
||||
public SimpleBooleanProperty getBankAccountChangedProperty()
|
||||
{
|
||||
return changedProperty;
|
||||
return bankAccountChangedProperty;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void triggerChange()
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
changedProperty.set(!changedProperty.get());
|
||||
return "User{" +
|
||||
"bankAccountChangedProperty=" + bankAccountChangedProperty +
|
||||
", accountID='" + accountID + '\'' +
|
||||
", messagePubKeyAsHex='" + messagePubKeyAsHex + '\'' +
|
||||
", isOnline=" + isOnline +
|
||||
", bankAccounts=" + bankAccounts +
|
||||
", currentBankAccount=" + currentBankAccount +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.bitsquare.msg;
|
||||
package io.bitsquare.util;
|
||||
|
||||
import io.bitsquare.util.Utils;
|
||||
import com.google.bitcoin.core.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -13,10 +13,10 @@ import java.security.spec.InvalidKeySpecException;
|
|||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class MsgKeyUtil
|
||||
public class DSAKeyUtil
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MsgKeyUtil.class);
|
||||
private static final String baseDir = Utils.getRootDir();
|
||||
private static final Logger log = LoggerFactory.getLogger(DSAKeyUtil.class);
|
||||
private static final String baseDir = Utilities.getRootDir();
|
||||
|
||||
public static KeyPair getKeyPair()
|
||||
{
|
||||
|
@ -28,6 +28,27 @@ public class MsgKeyUtil
|
|||
return getKeyPair("public_" + keyName + ".key", "private_" + keyName + ".key");
|
||||
}
|
||||
|
||||
|
||||
public static String getHexStringFromPublicKey(PublicKey publicKey)
|
||||
{
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return Utils.bytesToHexString(x509EncodedKeySpec.getEncoded());
|
||||
}
|
||||
|
||||
public static PublicKey getPublicKeyFromHexString(String publicKeyAsHex)
|
||||
{
|
||||
byte[] bytes = Utils.parseAsHexOrBase58(publicKeyAsHex);
|
||||
try
|
||||
{
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
return keyFactory.generatePublic(new X509EncodedKeySpec(bytes));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static KeyPair getKeyPair(String pubKeyPath, String privKeyPath)
|
||||
{
|
||||
try
|
||||
|
@ -40,7 +61,6 @@ public class MsgKeyUtil
|
|||
try
|
||||
{
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
|
||||
keyGen.initialize(1024);
|
||||
KeyPair generatedKeyPair = keyGen.genKeyPair();
|
||||
|
||||
|
@ -56,7 +76,6 @@ public class MsgKeyUtil
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static void dumpKeyPair(KeyPair keyPair)
|
||||
{
|
||||
PublicKey pub = keyPair.getPublic();
|
||||
|
@ -76,21 +95,20 @@ public class MsgKeyUtil
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static void saveKeyPair(String pubKeyPath, String privKeyPath, KeyPair keyPair) throws IOException
|
||||
{
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
|
||||
// Store Public Key.
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
|
||||
publicKey.getEncoded());
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
FileOutputStream fos = new FileOutputStream(baseDir + pubKeyPath);
|
||||
fos.write(x509EncodedKeySpec.getEncoded());
|
||||
fos.close();
|
||||
|
||||
// Store Private Key.
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
|
||||
privateKey.getEncoded());
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
|
||||
fos = new FileOutputStream(baseDir + privKeyPath);
|
||||
fos.write(pkcs8EncodedKeySpec.getEncoded());
|
||||
fos.close();
|
|
@ -1,8 +1,12 @@
|
|||
package io.bitsquare.util;
|
||||
|
||||
import com.google.bitcoin.core.ECKey;
|
||||
import io.bitsquare.bank.BankAccountType;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
|
||||
public class MockData
|
||||
|
@ -52,17 +56,32 @@ public class MockData
|
|||
public static List<Arbitrator> getArbitrators()
|
||||
{
|
||||
List<Arbitrator> list = new ArrayList<>();
|
||||
list.add(new Arbitrator("uid_1", "Charlie Boom", UUID.randomUUID().toString(),
|
||||
UUID.randomUUID().toString(), "http://www.arbit.io/Charly_Boom", 0.1, 10, com.google.bitcoin.core.Utils.toNanoCoins("0.01")));
|
||||
list.add(new Arbitrator("uid_2", "Tom Shang", UUID.randomUUID().toString(),
|
||||
UUID.randomUUID().toString(), "http://www.arbit.io/Tom_Shang", 0, 1, com.google.bitcoin.core.Utils.toNanoCoins("0.001")));
|
||||
list.add(new Arbitrator("uid_3", "Edward Snow", UUID.randomUUID().toString(),
|
||||
UUID.randomUUID().toString(), "http://www.arbit.io/Edward_Swow", 0.2, 5, com.google.bitcoin.core.Utils.toNanoCoins("0.05")));
|
||||
list.add(new Arbitrator("uid_4", "Julian Sander", UUID.randomUUID().toString(),
|
||||
UUID.randomUUID().toString(), "http://www.arbit.io/Julian_Sander", 0, 20, com.google.bitcoin.core.Utils.toNanoCoins("0.1")));
|
||||
list.add(new Arbitrator("uid_1", "Charlie Boom", com.google.bitcoin.core.Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Charly_Boom", 1, 10, com.google.bitcoin.core.Utils.toNanoCoins("0.01")));
|
||||
list.add(new Arbitrator("uid_2", "Tom Shang", com.google.bitcoin.core.Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Tom_Shang", 0, 1, com.google.bitcoin.core.Utils.toNanoCoins("0.001")));
|
||||
list.add(new Arbitrator("uid_3", "Edward Snow", com.google.bitcoin.core.Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Edward_Swow", 2, 5, com.google.bitcoin.core.Utils.toNanoCoins("0.05")));
|
||||
list.add(new Arbitrator("uid_4", "Julian Sander", com.google.bitcoin.core.Utils.bytesToHexString(new ECKey().getPubKey()),
|
||||
getMessagePubKey(), "http://www.arbit.io/Julian_Sander", 0, 20, com.google.bitcoin.core.Utils.toNanoCoins("0.1")));
|
||||
return list;
|
||||
}
|
||||
|
||||
private static String getMessagePubKey()
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
keyGen.initialize(1024);
|
||||
KeyPair generatedKeyPair = keyGen.genKeyPair();
|
||||
PublicKey pubKey = generatedKeyPair.getPublic();
|
||||
return DSAKeyUtil.getHexStringFromPublicKey(pubKey);
|
||||
} catch (Exception e2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Arbitrator> getRandomArbitrators()
|
||||
{
|
||||
return randomizeList(getArbitrators());
|
||||
|
@ -71,12 +90,12 @@ public class MockData
|
|||
|
||||
public static List<BankAccountType.BankAccountTypeEnum> getBankTransferTypeEnums()
|
||||
{
|
||||
return Utils.getAllBankAccountTypeEnums();
|
||||
return Utilities.getAllBankAccountTypeEnums();
|
||||
}
|
||||
|
||||
public static List<BankAccountType.BankAccountTypeEnum> getRandomBankTransferTypeEnums()
|
||||
{
|
||||
return randomizeList(Utils.getAllBankAccountTypeEnums());
|
||||
return randomizeList(Utilities.getAllBankAccountTypeEnums());
|
||||
}
|
||||
|
||||
public static List randomizeList(List list)
|
||||
|
|
|
@ -17,25 +17,25 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Utils
|
||||
public class Utilities
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(Utils.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
||||
|
||||
public static String getRootDir()
|
||||
{
|
||||
return Utils.class.getProtectionDomain().getCodeSource().getLocation().getFile() + "/";
|
||||
return Utilities.class.getProtectionDomain().getCodeSource().getLocation().getFile() + "/../";
|
||||
}
|
||||
|
||||
|
||||
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).setPrettyPrinting().create();
|
||||
return gson.toJson(object);
|
||||
}
|
||||
|
||||
public static <T> T jsonToObject(String jsonString, Class<T> classOfT)
|
||||
{
|
||||
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
|
||||
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
||||
return gson.fromJson(jsonString, classOfT);
|
||||
}
|
||||
|
||||
|
@ -228,23 +228,16 @@ public class Utils
|
|||
return bankTransferTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay in milliseconds
|
||||
* @param callback
|
||||
* @usage Utils.setTimeout(1000, (AnimationTimer animationTimer) -> {
|
||||
* doSomething();
|
||||
* return null;
|
||||
* });
|
||||
*/
|
||||
public static void setTimeout(int delay, Function<AnimationTimer, Void> callback)
|
||||
public static AnimationTimer setTimeout(int delay, Function<AnimationTimer, Void> callback)
|
||||
{
|
||||
long startTime = System.currentTimeMillis();
|
||||
AnimationTimer animationTimer = new AnimationTimer()
|
||||
{
|
||||
long lastTimeStamp = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void handle(long arg0)
|
||||
{
|
||||
if (System.currentTimeMillis() > delay + startTime)
|
||||
if (System.currentTimeMillis() > delay + lastTimeStamp)
|
||||
{
|
||||
callback.apply(this);
|
||||
this.stop();
|
||||
|
@ -252,5 +245,26 @@ public class Utils
|
|||
}
|
||||
};
|
||||
animationTimer.start();
|
||||
return animationTimer;
|
||||
}
|
||||
|
||||
public static AnimationTimer setInterval(int delay, Function<AnimationTimer, Void> callback)
|
||||
{
|
||||
AnimationTimer animationTimer = new AnimationTimer()
|
||||
{
|
||||
long lastTimeStamp = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void handle(long arg0)
|
||||
{
|
||||
if (System.currentTimeMillis() > delay + lastTimeStamp)
|
||||
{
|
||||
lastTimeStamp = System.currentTimeMillis();
|
||||
callback.apply(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
animationTimer.start();
|
||||
return animationTimer;
|
||||
}
|
||||
}
|
BIN
src/main/resources/images/nav/alertRound.png
Normal file
After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 901 B After Width: | Height: | Size: 901 B |
BIN
src/main/resources/images/unused/alert.png
Normal file
After Width: | Height: | Size: 272 B |
BIN
src/main/resources/images/unused/alertFlag1.png
Normal file
After Width: | Height: | Size: 259 B |
BIN
src/main/resources/images/unused/alertFlag2.png
Normal file
After Width: | Height: | Size: 262 B |
BIN
src/main/resources/images/unused/arrowIn.png
Normal file
After Width: | Height: | Size: 233 B |
Before Width: | Height: | Size: 389 B |
|
@ -13,10 +13,10 @@
|
|||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.bitsquare" level="INFO"/>
|
||||
<logger name="io.bitsquare" level="DEBUG"/>
|
||||
|
||||
<logger name="com.google.bitcoin" level="ERROR"/>
|
||||
<logger name="net.tomp2p" level="INFO"/>
|
||||
<logger name="net.tomp2p" level="ERROR"/>
|
||||
|
||||
|
||||
<logger name="com.google.bitcoin.core.Peer" level="ERROR" additivity="false"/>
|
||||
|
|