changed trade protocol to use tasks, added scheduler framework

This commit is contained in:
Manfred Karrer 2014-07-03 19:22:06 +02:00
parent 4f26d76746
commit 7e55b7325a
93 changed files with 3980 additions and 1273 deletions

17
pom.xml
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -194,6 +194,19 @@
<artifactId>proguard</artifactId> <artifactId>proguard</artifactId>
<version>4.4</version> <version>4.4</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency>
-->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.3</version>
</dependency>
<!--
<dependency>
<groupId>com.netflix.rxjava</groupId>
<artifactId>rxjava-core</artifactId>
<version>0.5.3</version>
</dependency> </dependency>
--> -->
</dependencies> </dependencies>

View file

@ -17,7 +17,7 @@ public class BtcFormatter
public static BigInteger mBTC = new BigInteger("100000"); public static BigInteger mBTC = new BigInteger("100000");
public static String satoshiToString(BigInteger value) public static String formatSatoshis(BigInteger value)
{ {
return Utils.bitcoinValueToFriendlyString(value); return Utils.bitcoinValueToFriendlyString(value);
} }
@ -30,7 +30,14 @@ public class BtcFormatter
public static BigInteger stringValueToSatoshis(String value) public static BigInteger stringValueToSatoshis(String value)
{ {
return Utils.toNanoCoins(String.valueOf(BitSquareConverter.stringToDouble2(value))); try
{
return Utils.toNanoCoins(String.valueOf(BitSquareConverter.stringToDouble(value)));
} catch (ArithmeticException e)
{
log.warn("ArithmeticException " + e);
}
return BigInteger.ZERO;
} }
public static BigInteger doubleValueToSatoshis(double value) public static BigInteger doubleValueToSatoshis(double value)

View file

@ -19,7 +19,7 @@ public class BtcValidator
public static boolean isMinSpendableAmount(BigInteger amount) public static boolean isMinSpendableAmount(BigInteger amount)
{ {
return amount != null && amount.compareTo(FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT)) > 0; return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
} }
public boolean isAddressValid(String addressString) public boolean isAddressValid(String addressString)

View file

@ -2,22 +2,16 @@ package io.bitsquare.btc;
import com.google.bitcoin.core.*; import com.google.bitcoin.core.*;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.currency.Bitcoin;
import java.math.BigInteger; import java.math.BigInteger;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class FeePolicy public class FeePolicy
{ {
public static final BigInteger TX_FEE_depr = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; public static final BigInteger TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
public static final BigInteger ACCOUNT_REGISTRATION_FEE_depr = Utils.toNanoCoins("0.01"); public static final BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.01");
public static final BigInteger CREATE_OFFER_FEE_depr = Utils.toNanoCoins("0.001"); public static final BigInteger CREATE_OFFER_FEE = Utils.toNanoCoins("0.001");
public static final BigInteger TAKE_OFFER_FEE_depr = CREATE_OFFER_FEE_depr; public static final BigInteger TAKE_OFFER_FEE = CREATE_OFFER_FEE;
public static final Bitcoin TX_FEE = new Bitcoin(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
public static final Bitcoin CREATE_OFFER_FEE = new Bitcoin(Utils.toNanoCoins("0.001"));
public static final Bitcoin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
private static final Logger log = LoggerFactory.getLogger(FeePolicy.class); private static final Logger log = LoggerFactory.getLogger(FeePolicy.class);
private static final String registrationFeeAddress = "mvkDXt4QmN4Nq9dRUsRigBCaovde9nLkZR"; private static final String registrationFeeAddress = "mvkDXt4QmN4Nq9dRUsRigBCaovde9nLkZR";
private static final String createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg"; private static final String createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";

View file

@ -86,7 +86,7 @@ public class WalletFacade
public void initWallet() public void initWallet()
{ {
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means // Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener // we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in // we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
// a future version. // a future version.
@ -94,7 +94,7 @@ public class WalletFacade
if (params == RegTestParams.get()) if (params == RegTestParams.get())
{ {
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally. walletAppKit.connectToLocalHost(); // You should execute a regtest mode bitcoind locally.
} }
else if (params == MainNetParams.get()) else if (params == MainNetParams.get())
{ {
@ -527,21 +527,21 @@ public class WalletFacade
public boolean isRegistrationFeeBalanceSufficient() public boolean isRegistrationFeeBalanceSufficient()
{ {
return getRegistrationBalance().compareTo(FeePolicy.ACCOUNT_REGISTRATION_FEE_depr) >= 0; return getRegistrationBalance().compareTo(FeePolicy.ACCOUNT_REGISTRATION_FEE) >= 0;
} }
public boolean isUnusedTradeAddressBalanceAboveCreationFee() public boolean isUnusedTradeAddressBalanceAboveCreationFee()
{ {
AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo(); AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo();
BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress()); BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress());
return unUsedAddressInfoBalance.compareTo(FeePolicy.CREATE_OFFER_FEE_depr) > 0; return unUsedAddressInfoBalance.compareTo(FeePolicy.CREATE_OFFER_FEE) > 0;
} }
public boolean isUnusedTradeAddressBalanceAboveTakeOfferFee() public boolean isUnusedTradeAddressBalanceAboveTakeOfferFee()
{ {
AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo(); AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo();
BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress()); BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress());
return unUsedAddressInfoBalance.compareTo(FeePolicy.TAKE_OFFER_FEE_depr) > 0; return unUsedAddressInfoBalance.compareTo(FeePolicy.TAKE_OFFER_FEE) > 0;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -573,8 +573,8 @@ public class WalletFacade
byte[] data = cryptoFacade.getEmbeddedAccountRegistrationData(getRegistrationAddressInfo().getKey(), stringifiedBankAccounts); byte[] data = cryptoFacade.getEmbeddedAccountRegistrationData(getRegistrationAddressInfo().getKey(), stringifiedBankAccounts);
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build()); tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build());
BigInteger fee = FeePolicy.ACCOUNT_REGISTRATION_FEE_depr.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(FeePolicy.TX_FEE_depr); BigInteger fee = FeePolicy.ACCOUNT_REGISTRATION_FEE.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.satoshiToString(fee)); log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForRegistrationFee()); tx.addOutput(fee, feePolicy.getAddressForRegistrationFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -588,12 +588,11 @@ public class WalletFacade
printInputs("payRegistrationFee", tx); printInputs("payRegistrationFee", tx);
} }
@SuppressWarnings("UnusedReturnValue")
public String payCreateOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException public String payCreateOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
BigInteger fee = FeePolicy.CREATE_OFFER_FEE_depr.subtract(FeePolicy.TX_FEE_depr); BigInteger fee = FeePolicy.CREATE_OFFER_FEE.subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.satoshiToString(fee)); log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForCreateOfferFee()); tx.addOutput(fee, feePolicy.getAddressForCreateOfferFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -603,18 +602,18 @@ public class WalletFacade
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback); Futures.addCallback(sendResult.broadcastComplete, callback);
printInputs("payTakeOfferFee", tx); printInputs("payCreateOfferFee", tx);
log.debug("tx=" + tx); log.debug("tx=" + tx);
return tx.getHashAsString(); return tx.getHashAsString();
} }
@SuppressWarnings("UnusedReturnValue")
public String payTakeOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException public String payTakeOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
BigInteger fee = FeePolicy.TAKE_OFFER_FEE_depr.subtract(FeePolicy.TX_FEE_depr); BigInteger fee = FeePolicy.TAKE_OFFER_FEE.subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.satoshiToString(fee)); log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForTakeOfferFee()); tx.addOutput(fee, feePolicy.getAddressForTakeOfferFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -643,7 +642,7 @@ public class WalletFacade
FutureCallback<Transaction> callback) throws AddressFormatException, InsufficientMoneyException, IllegalArgumentException FutureCallback<Transaction> callback) throws AddressFormatException, InsufficientMoneyException, IllegalArgumentException
{ {
Transaction tx = new Transaction(params); Transaction tx = new Transaction(params);
tx.addOutput(amount.subtract(FeePolicy.TX_FEE_depr), new Address(params, withdrawToAddress)); tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, withdrawToAddress));
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation) // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
@ -672,7 +671,7 @@ public class WalletFacade
{ {
log.debug("offererCreatesMSTxAndAddPayment"); log.debug("offererCreatesMSTxAndAddPayment");
log.trace("inputs: "); log.trace("inputs: ");
log.trace("offererInputAmount=" + BtcFormatter.satoshiToString(offererInputAmount)); log.trace("offererInputAmount=" + BtcFormatter.formatSatoshis(offererInputAmount));
log.trace("offererPubKey=" + offererPubKey); log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey); log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey); log.trace("arbitratorPubKey=" + arbitratorPubKey);
@ -730,8 +729,8 @@ public class WalletFacade
{ {
log.debug("takerAddPaymentAndSignTx"); log.debug("takerAddPaymentAndSignTx");
log.trace("inputs: "); log.trace("inputs: ");
log.trace("takerInputAmount=" + BtcFormatter.satoshiToString(takerInputAmount)); log.trace("takerInputAmount=" + BtcFormatter.formatSatoshis(takerInputAmount));
log.trace("msOutputAmount=" + BtcFormatter.satoshiToString(msOutputAmount)); log.trace("msOutputAmount=" + BtcFormatter.formatSatoshis(msOutputAmount));
log.trace("offererPubKey=" + offererPubKey); log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey); log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey); log.trace("arbitratorPubKey=" + arbitratorPubKey);
@ -786,7 +785,7 @@ public class WalletFacade
tx.addOutput(tempTx.getOutput(1)); tx.addOutput(tempTx.getOutput(1));
// We add the btc tx fee to the msOutputAmount and apply the change to the multiSig output // We add the btc tx fee to the msOutputAmount and apply the change to the multiSig output
msOutputAmount = msOutputAmount.add(FeePolicy.TX_FEE_depr); msOutputAmount = msOutputAmount.add(FeePolicy.TX_FEE);
tx.getOutput(0).setValue(msOutputAmount); tx.getOutput(0).setValue(msOutputAmount);
// Now we sign our input // Now we sign our input
@ -835,7 +834,7 @@ public class WalletFacade
// 3. step: deposit tx // 3. step: deposit tx
// Offerer signs tx and publishes it // Offerer signs tx and publishes it
public Transaction offererSignAndPublishTx(String offerersFirstTxAsHex, public void offererSignAndPublishTx(String offerersFirstTxAsHex,
String takersSignedTxAsHex, String takersSignedTxAsHex,
String takersSignedConnOutAsHex, String takersSignedConnOutAsHex,
String takersSignedScriptSigAsHex, String takersSignedScriptSigAsHex,
@ -881,7 +880,7 @@ public class WalletFacade
takersSignedTxInput.setParent(tx); takersSignedTxInput.setParent(tx);
tx.addInput(takersSignedTxInput); tx.addInput(takersSignedTxInput);
//TODO handle non change output cases //TODO onResult non change output cases
// add outputs from takers tx, they are already correct // add outputs from takers tx, they are already correct
tx.addOutput(takersSignedTx.getOutput(0)); tx.addOutput(takersSignedTx.getOutput(0));
if (takersSignedTx.getOutputs().size() > 1) if (takersSignedTx.getOutputs().size() > 1)
@ -942,7 +941,6 @@ public class WalletFacade
Futures.addCallback(broadcastComplete, callback); Futures.addCallback(broadcastComplete, callback);
printInputs("tx", tx); printInputs("tx", tx);
log.debug("tx = " + tx); log.debug("tx = " + tx);
return tx;
} }
// 4 step deposit tx: Offerer send deposit tx to taker // 4 step deposit tx: Offerer send deposit tx to taker
@ -981,8 +979,8 @@ public class WalletFacade
log.debug("offererCreatesAndSignsPayoutTx"); log.debug("offererCreatesAndSignsPayoutTx");
log.trace("inputs: "); log.trace("inputs: ");
log.trace("depositTxID=" + depositTxID); log.trace("depositTxID=" + depositTxID);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount)); log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount)); log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("takerAddress=" + takerAddress); log.trace("takerAddress=" + takerAddress);
// Offerer has published depositTx earlier, so he has it in his wallet // Offerer has published depositTx earlier, so he has it in his wallet
@ -1007,9 +1005,7 @@ public class WalletFacade
} }
// 6. step payout tx: Taker signs and publish tx // 6. step payout tx: Taker signs and publish tx
@SuppressWarnings("UnusedReturnValue") public void takerSignsAndSendsTx(String depositTxAsHex,
public Transaction takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR, String offererSignatureR,
String offererSignatureS, String offererSignatureS,
BigInteger offererPaybackAmount, BigInteger offererPaybackAmount,
@ -1023,8 +1019,8 @@ public class WalletFacade
log.trace("depositTxAsHex=" + depositTxAsHex); log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererSignatureR=" + offererSignatureR); log.trace("offererSignatureR=" + offererSignatureR);
log.trace("offererSignatureS=" + offererSignatureS); log.trace("offererSignatureS=" + offererSignatureS);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount)); log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount)); log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress); log.trace("offererAddress=" + offererAddress);
log.trace("callback=" + callback); log.trace("callback=" + callback);
@ -1062,7 +1058,6 @@ public class WalletFacade
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent()); log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("takerSignsAndSendsTx", tx); printInputs("takerSignsAndSendsTx", tx);
log.debug("tx = " + tx); log.debug("tx = " + tx);
return tx;
} }
@ -1103,8 +1098,8 @@ public class WalletFacade
log.trace("createPayoutTx"); log.trace("createPayoutTx");
log.trace("inputs: "); log.trace("inputs: ");
log.trace("depositTxAsHex=" + depositTxAsHex); log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount)); log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount)); log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress); log.trace("offererAddress=" + offererAddress);
log.trace("takerAddress=" + takerAddress); log.trace("takerAddress=" + takerAddress);
@ -1122,7 +1117,7 @@ public class WalletFacade
{ {
for (TransactionInput input : tx.getInputs()) for (TransactionInput input : tx.getInputs())
if (input.getConnectedOutput() != null) if (input.getConnectedOutput() != null)
log.trace(tracePrefix + ": " + BtcFormatter.satoshiToString(input.getConnectedOutput().getValue())); log.trace(tracePrefix + " input value : " + BtcFormatter.formatSatoshis(input.getConnectedOutput().getValue()));
else else
log.trace(tracePrefix + ": " + "Transaction already has inputs but we don't have the connected outputs, so we don't know the value."); log.trace(tracePrefix + ": " + "Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
} }

View file

@ -1,110 +0,0 @@
package io.bitsquare.currency;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.btc.BtcFormatter;
import java.math.BigInteger;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Bitcoin extends BigInteger
{
private static final Logger log = LoggerFactory.getLogger(Bitcoin.class);
private static final long serialVersionUID = 6436341706716520132L;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Bitcoin(BigInteger val)
{
super(val.toByteArray());
}
public Bitcoin(byte[] val)
{
super(val);
}
public Bitcoin(int signum, byte[] magnitude)
{
super(signum, magnitude);
}
public Bitcoin(String val, int radix)
{
super(val, radix);
}
public Bitcoin(String val)
{
super(val);
}
public Bitcoin(int numBits, Random rnd)
{
super(numBits, rnd);
}
public Bitcoin(int bitLength, int certainty, Random rnd)
{
super(bitLength, certainty, rnd);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public boolean isZero()
{
return this.compareTo(BigInteger.ZERO) == 0;
}
public boolean isMinValue()
{
return this.compareTo(Transaction.MIN_NONDUST_OUTPUT) >= 0;
}
public Bitcoin addBitcoin(Bitcoin other)
{
return new Bitcoin(this.add(other));
}
public Bitcoin subtractBitcoin(Bitcoin other)
{
return new Bitcoin(this.subtract(other));
}
public Bitcoin multiplyBitcoin(Bitcoin other)
{
return new Bitcoin(this.multiply(other));
}
public boolean isLarger(Bitcoin other)
{
return this.compareTo(other) > 0;
}
public boolean isLess(Bitcoin other)
{
return this.compareTo(other) < 0;
}
public boolean isEqual(Bitcoin other)
{
return this.compareTo(other) == 0;
}
public String getFormattedValue()
{
return BtcFormatter.satoshiToString(this);
}
}

View file

@ -1,51 +0,0 @@
package io.bitsquare.currency;
import io.bitsquare.gui.util.BitSquareFormatter;
import java.util.Currency;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Fiat
{
private static final Logger log = LoggerFactory.getLogger(Fiat.class);
private double value;
private Currency currency;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Fiat(double value)
{
this.value = value;
this.currency = Currency.getInstance(Locale.getDefault());
}
public Fiat(double value, Currency currency)
{
this.value = value;
this.currency = currency;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public String getFormattedValue()
{
return BitSquareFormatter.formatPrice(value);
}
public String getCurrencyCode()
{
return currency.getCurrencyCode();
}
public Currency getCurrency()
{
return currency;
}
}

View file

@ -8,11 +8,11 @@ import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.di.GuiceFXMLLoader; import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.components.NetworkSyncPane; import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.market.MarketController; import io.bitsquare.gui.market.MarketController;
import io.bitsquare.gui.orders.OrdersController;
import io.bitsquare.gui.util.Icons; import io.bitsquare.gui.util.Icons;
import io.bitsquare.gui.util.Transitions; import io.bitsquare.gui.util.Transitions;
import io.bitsquare.locale.Localisation; import io.bitsquare.locale.Localisation;
import io.bitsquare.msg.MessageFacade; import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Direction; import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Trading; import io.bitsquare.trade.Trading;
@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
public class MainController implements Initializable, NavigationController public class MainController implements Initializable, NavigationController
{ {
private static final Logger log = LoggerFactory.getLogger(MainController.class); private static final Logger log = LoggerFactory.getLogger(MainController.class);
private static MainController mainController; private static MainController INSTANCE;
private final User user; private final User user;
private final WalletFacade walletFacade; private final WalletFacade walletFacade;
@ -85,7 +85,7 @@ public class MainController implements Initializable, NavigationController
this.trading = trading; this.trading = trading;
this.storage = storage; this.storage = storage;
MainController.mainController = this; MainController.INSTANCE = this;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -93,9 +93,9 @@ public class MainController implements Initializable, NavigationController
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public static MainController INSTANCE() public static MainController GET_INSTANCE()
{ {
return mainController; return INSTANCE;
} }
@ -173,7 +173,7 @@ public class MainController implements Initializable, NavigationController
private void init() private void init()
{ {
messageFacade.init(); messageFacade.init();
messageFacade.addTakeOfferRequestListener(this::showTakeOfferRequest); messageFacade.addTakeOfferRequestListener(this::onTakeOfferRequested);
walletFacade.addDownloadListener(new WalletFacade.DownloadListener() walletFacade.addDownloadListener(new WalletFacade.DownloadListener()
{ {
@ -209,13 +209,15 @@ public class MainController implements Initializable, NavigationController
} }
private void showTakeOfferRequest(TradeMessage tradeMessage, PeerAddress sender) private void onTakeOfferRequested(String offerId, PeerAddress sender)
{ {
trading.createOffererPaymentProtocol(tradeMessage, sender);
final Button alertButton = new Button("", Icons.getIconImageView(Icons.MSG_ALERT)); final Button alertButton = new Button("", Icons.getIconImageView(Icons.MSG_ALERT));
alertButton.setId("nav-alert-button"); alertButton.setId("nav-alert-button");
alertButton.relocate(36, 19); alertButton.relocate(36, 19);
alertButton.setOnAction((e) -> ordersButton.fire()); alertButton.setOnAction((e) -> {
OrdersController.GET_INSTANCE().setSelectedTabIndex(1);
ordersButton.fire();
});
Tooltip.install(alertButton, new Tooltip("Someone accepted your offer")); Tooltip.install(alertButton, new Tooltip("Someone accepted your offer"));
ordersButtonButtonHolder.getChildren().add(alertButton); ordersButtonButtonHolder.getChildren().add(alertButton);
} }
@ -289,13 +291,13 @@ public class MainController implements Initializable, NavigationController
balanceTextField.setEditable(false); balanceTextField.setEditable(false);
balanceTextField.setPrefWidth(90); balanceTextField.setPrefWidth(90);
balanceTextField.setId("nav-balance-label"); balanceTextField.setId("nav-balance-label");
balanceTextField.setText(BtcFormatter.satoshiToString(walletFacade.getWalletBalance())); balanceTextField.setText(BtcFormatter.formatSatoshis(walletFacade.getWalletBalance()));
walletFacade.addBalanceListener(new BalanceListener() walletFacade.addBalanceListener(new BalanceListener()
{ {
@Override @Override
public void onBalanceChanged(BigInteger balance) public void onBalanceChanged(BigInteger balance)
{ {
balanceTextField.setText(BtcFormatter.satoshiToString(balance)); balanceTextField.setText(BtcFormatter.formatSatoshis(balance));
} }
}); });

View file

@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Node; import javafx.scene.Node;
@ -181,7 +180,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@FXML @FXML
public void onPrevious(ActionEvent actionEvent) public void onPrevious()
{ {
if (index > 0) if (index > 0)
{ {
@ -193,7 +192,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
} }
@FXML @FXML
public void onNext(ActionEvent actionEvent) public void onNext()
{ {
if (index < allArbitrators.size() - 1) if (index < allArbitrators.size() - 1)
{ {
@ -205,14 +204,14 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
} }
@FXML @FXML
public void onSelect(ActionEvent actionEvent) public void onSelect()
{ {
settings.addAcceptedArbitrator(currentArbitrator); settings.addAcceptedArbitrator(currentArbitrator);
storage.write(settings.getClass().getName(), settings); storage.write(settings.getClass().getName(), settings);
} }
@FXML @FXML
public void onClose(ActionEvent actionEvent) public void onClose()
{ {
Stage stage = (Stage) rootContainer.getScene().getWindow(); Stage stage = (Stage) rootContainer.getScene().getWindow();
stage.close(); stage.close();

View file

@ -29,7 +29,6 @@ import java.math.BigInteger;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
@ -232,7 +231,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@FXML @FXML
public void onSelectIDType(ActionEvent actionEvent) public void onSelectIDType()
{ {
idType = idTypeComboBox.getSelectionModel().getSelectedItem(); idType = idTypeComboBox.getSelectionModel().getSelectedItem();
if (idType != null) if (idType != null)
@ -259,7 +258,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
} }
@FXML @FXML
public void onAddLanguage(ActionEvent actionEvent) public void onAddLanguage()
{ {
Locale item = languageComboBox.getSelectionModel().getSelectedItem(); Locale item = languageComboBox.getSelectionModel().getSelectedItem();
if (!languageList.contains(item) && item != null) if (!languageList.contains(item) && item != null)
@ -271,14 +270,14 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
} }
@FXML @FXML
public void onClearLanguages(ActionEvent actionEvent) public void onClearLanguages()
{ {
languageList.clear(); languageList.clear();
languagesTextField.setText(""); languagesTextField.setText("");
} }
@FXML @FXML
public void onAddMethod(ActionEvent actionEvent) public void onAddMethod()
{ {
Arbitrator.METHOD item = methodsComboBox.getSelectionModel().getSelectedItem(); Arbitrator.METHOD item = methodsComboBox.getSelectionModel().getSelectedItem();
if (!methodList.contains(item) && item != null) if (!methodList.contains(item) && item != null)
@ -290,7 +289,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
} }
@FXML @FXML
public void onClearMethods(ActionEvent actionEvent) public void onClearMethods()
{ {
methodList.clear(); methodList.clear();
methodsTextField.setText(""); methodsTextField.setText("");
@ -298,7 +297,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
@FXML @FXML
public void onAddIDVerification(ActionEvent actionEvent) public void onAddIDVerification()
{ {
Arbitrator.ID_VERIFICATION idVerification = idVerificationsComboBox.getSelectionModel().getSelectedItem(); Arbitrator.ID_VERIFICATION idVerification = idVerificationsComboBox.getSelectionModel().getSelectedItem();
if (idVerification != null) if (idVerification != null)
@ -314,14 +313,14 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
} }
@FXML @FXML
public void onClearIDVerifications(ActionEvent actionEvent) public void onClearIDVerifications()
{ {
idVerificationList.clear(); idVerificationList.clear();
idVerificationsTextField.setText(""); idVerificationsTextField.setText("");
} }
@FXML @FXML
public void onSaveProfile(ActionEvent actionEvent) public void onSaveProfile()
{ {
arbitrator = getEditedArbitrator(); arbitrator = getEditedArbitrator();
if (arbitrator != null) if (arbitrator != null)
@ -349,7 +348,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
} }
@FXML @FXML
public void onPaymentDone(ActionEvent actionEvent) public void onPaymentDone()
{ {
//To change body of created methods use File | Settings | File Templates. //To change body of created methods use File | Settings | File Templates.
} }

View file

@ -13,9 +13,12 @@ import javafx.scene.Node;
import javafx.scene.control.SingleSelectionModel; import javafx.scene.control.SingleSelectionModel;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LazyLoadingTabPane extends TabPane public class LazyLoadingTabPane extends TabPane
{ {
private static final Logger log = LoggerFactory.getLogger(LazyLoadingTabPane.class);
private final Map<Integer, Node> views = new HashMap<>(); private final Map<Integer, Node> views = new HashMap<>();
private final Map<Integer, ChildController> controllers = new HashMap<>(); private final Map<Integer, ChildController> controllers = new HashMap<>();
private SingleSelectionModel<Tab> selectionModel; private SingleSelectionModel<Tab> selectionModel;
@ -24,6 +27,7 @@ public class LazyLoadingTabPane extends TabPane
private String[] tabContentFXMLUrls; private String[] tabContentFXMLUrls;
private Storage storage; private Storage storage;
private ChildController childController; private ChildController childController;
private int selectedTabIndex = -1;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -50,9 +54,17 @@ public class LazyLoadingTabPane extends TabPane
selectionModel = getSelectionModel(); selectionModel = getSelectionModel();
selectionModel.selectedItemProperty().addListener((observableValue, oldTab, newTab) -> onTabSelectedIndexChanged()); selectionModel.selectedItemProperty().addListener((observableValue, oldTab, newTab) -> onTabSelectedIndexChanged());
if (selectedTabIndex == -1)
{
Object indexObject = storage.read(storageId); Object indexObject = storage.read(storageId);
log.trace("saved index" + indexObject);
if (indexObject != null) if (indexObject != null)
selectionModel.select((int) indexObject); selectionModel.select((int) indexObject);
}
else
{
selectionModel.select(selectedTabIndex);
}
onTabSelectedIndexChanged(); onTabSelectedIndexChanged();
} }
@ -81,6 +93,7 @@ public class LazyLoadingTabPane extends TabPane
private void onTabSelectedIndexChanged() private void onTabSelectedIndexChanged()
{ {
int index = selectionModel.getSelectedIndex(); int index = selectionModel.getSelectedIndex();
log.trace("onTabSelectedIndexChanged index" + index);
if (index < tabContentFXMLUrls.length && index >= 0) if (index < tabContentFXMLUrls.length && index >= 0)
{ {
if (childController != null) if (childController != null)
@ -121,4 +134,8 @@ public class LazyLoadingTabPane extends TabPane
} }
} }
public void setSelectedTabIndex(int selectedTabIndex)
{
this.selectedTabIndex = selectedTabIndex;
}
} }

View file

@ -0,0 +1,212 @@
package io.bitsquare.gui.components;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.paint.Color;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* TextField with regex-based real-time input validation.
* JavaFX 2 and FXML compatible. </p>
* <p>
* FXML code example:<div>
* {@code <ValidatedTextField fx:id="validatedTextField" minLength="1" maxLength="1" mask="^[0-9]*$" />}
* </div>
* </p>
*/
public class ValidatedTextField extends TextField
{
private static final Logger log = LoggerFactory.getLogger(ValidatedTextField.class);
private final BooleanProperty invalid = new SimpleBooleanProperty(false);
private final StringProperty mask;
private final IntegerProperty minLength;
private final IntegerProperty maxLength;
private Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0);
public ValidatedTextField()
{
super();
this.mask = new SimpleStringProperty("^[0-9.,]*$");
this.minLength = new SimpleIntegerProperty(1);
this.maxLength = new SimpleIntegerProperty(12);
bind();
}
public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable)
{
this(mask, minLength, maxLength, nullable, null);
}
public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable, String string)
{
super(string);
this.mask = new SimpleStringProperty(mask);
this.minLength = new SimpleIntegerProperty(minLength);
this.maxLength = new SimpleIntegerProperty(maxLength);
bind();
}
public ReadOnlyBooleanProperty invalidProperty()
{
return invalid;
}
public ReadOnlyStringProperty maskProperty()
{
return mask;
}
public ReadOnlyIntegerProperty minLengthProperty()
{
return minLength;
}
public ReadOnlyIntegerProperty maxLengthProperty()
{
return maxLength;
}
public boolean isInvalid()
{
return invalid.get();
}
public String getMask()
{
return mask.get();
}
public void setMask(String mask)
{
this.mask.set(mask);
}
public int getMinLength()
{
return minLength.get();
}
public void setMinLength(int minLength)
{
this.minLength.set(minLength);
}
public int getMaxLength()
{
return maxLength.get();
}
public void setMaxLength(int maxLength)
{
this.maxLength.set(maxLength);
}
public Effect getInvalidEffect()
{
return this.invalidEffect;
}
public void setInvalidEffect(Effect effect)
{
this.invalidEffect = effect;
}
private void bind()
{
this.invalid.bind(maskCheck().or(minLengthCheck()));
this.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> ov, String t, String t1)
{
if (textProperty().get().length() > maxLength.get())
{
setText(t);
}
}
});
this.invalid.addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1)
{
if (t ^ t1)
{
if (t1)
{
// setStyle("-fx-font-weight: bold; -fx-text-fill: red;");
setEffect(invalidEffect);
}
else
{
// setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;");
setEffect(null);
}
}
}
});
}
private BooleanBinding maskCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), mask);
}
@Override
protected boolean computeValue()
{
return !textProperty().get().matches(mask.get());
}
};
}
private BooleanBinding minLengthCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), minLength);
}
@Override
protected boolean computeValue()
{
return textProperty().get().length() < minLength.get();
}
};
}
private BooleanBinding maxLengthCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), maxLength);
}
@Override
protected boolean computeValue()
{
return textProperty().get().length() > maxLength.get();
}
};
}
}

View file

@ -40,7 +40,7 @@ public class TransactionsListItem
Address address = null; Address address = null;
if (valueSentToMe.compareTo(BigInteger.ZERO) == 0) if (valueSentToMe.compareTo(BigInteger.ZERO) == 0)
{ {
amount.set("-" + BtcFormatter.satoshiToString(valueSentFromMe)); amount.set("-" + BtcFormatter.formatSatoshis(valueSentFromMe));
for (TransactionOutput transactionOutput : transaction.getOutputs()) for (TransactionOutput transactionOutput : transaction.getOutputs())
{ {
@ -62,7 +62,7 @@ public class TransactionsListItem
} }
else if (valueSentFromMe.compareTo(BigInteger.ZERO) == 0) else if (valueSentFromMe.compareTo(BigInteger.ZERO) == 0)
{ {
amount.set(BtcFormatter.satoshiToString(valueSentToMe)); amount.set(BtcFormatter.formatSatoshis(valueSentToMe));
type.set("Received with"); type.set("Received with");
for (TransactionOutput transactionOutput : transaction.getOutputs()) for (TransactionOutput transactionOutput : transaction.getOutputs())
@ -83,7 +83,7 @@ public class TransactionsListItem
} }
else else
{ {
amount.set(BtcFormatter.satoshiToString(valueSentToMe.subtract(valueSentFromMe))); amount.set(BtcFormatter.formatSatoshis(valueSentToMe.subtract(valueSentFromMe)));
boolean outgoing = false; boolean outgoing = false;
for (TransactionOutput transactionOutput : transaction.getOutputs()) for (TransactionOutput transactionOutput : transaction.getOutputs())

View file

@ -83,7 +83,7 @@ public class WithdrawalController implements Initializable, ChildController, Hib
if (BigInteger.ZERO.compareTo(newValue.getBalance()) <= 0) if (BigInteger.ZERO.compareTo(newValue.getBalance()) <= 0)
{ {
amountTextField.setText(BtcFormatter.satoshiToString(newValue.getBalance())); amountTextField.setText(BtcFormatter.formatSatoshis(newValue.getBalance()));
withdrawFromTextField.setText(newValue.getAddressEntry().getAddressString()); withdrawFromTextField.setText(newValue.getAddressEntry().getAddressString());
changeAddressTextField.setText(newValue.getAddressEntry().getAddressString()); changeAddressTextField.setText(newValue.getAddressEntry().getAddressString());
} }
@ -174,8 +174,8 @@ public class WithdrawalController implements Initializable, ChildController, Hib
"Amount: " + amountTextField.getText() + " BTC\n" + "Amount: " + amountTextField.getText() + " BTC\n" +
"Sending address: " + withdrawFromTextField.getText() + "\n" + "Sending address: " + withdrawFromTextField.getText() + "\n" +
"Receiving address: " + withdrawToTextField.getText() + "\n" + "Receiving address: " + withdrawToTextField.getText() + "\n" +
"Transaction fee: " + BtcFormatter.satoshiToString(FeePolicy.TX_FEE_depr) + "\n" + "Transaction fee: " + BtcFormatter.formatSatoshis(FeePolicy.TX_FEE) + "\n" +
"You receive in total: " + BtcFormatter.satoshiToString(amount.subtract(FeePolicy.TX_FEE_depr)) + " BTC\n\n" + "You receive in total: " + BtcFormatter.formatSatoshis(amount.subtract(FeePolicy.TX_FEE)) + " BTC\n\n" +
"Are you sure you withdraw that amount?"); "Are you sure you withdraw that amount?");
if (response == Dialog.Actions.OK) if (response == Dialog.Actions.OK)
{ {

View file

@ -84,7 +84,7 @@ public class WithdrawalListItem
this.balance = balance; this.balance = balance;
if (balance != null) if (balance != null)
{ {
balanceLabel.setText(BtcFormatter.satoshiToString(balance)); balanceLabel.setText(BtcFormatter.formatSatoshis(balance));
} }
} }

View file

@ -34,8 +34,6 @@ public class MarketController implements Initializable, NavigationController, Ch
public void initialize(URL url, ResourceBundle rb) public void initialize(URL url, ResourceBundle rb)
{ {
navigateToView(NavigationItem.ORDER_BOOK); navigateToView(NavigationItem.ORDER_BOOK);
navigateToView(NavigationItem.TAKE_OFFER);
} }

View file

@ -127,7 +127,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
} }
acceptedCountriesTextField.setText(BitSquareFormatter.countryLocalesToString(settings.getAcceptedCountries())); acceptedCountriesTextField.setText(BitSquareFormatter.countryLocalesToString(settings.getAcceptedCountries()));
acceptedLanguagesTextField.setText(BitSquareFormatter.languageLocalesToString(settings.getAcceptedLanguageLocales())); acceptedLanguagesTextField.setText(BitSquareFormatter.languageLocalesToString(settings.getAcceptedLanguageLocales()));
feeLabel.setText(BtcFormatter.satoshiToString(FeePolicy.CREATE_OFFER_FEE_depr)); feeLabel.setText(BtcFormatter.formatSatoshis(FeePolicy.CREATE_OFFER_FEE));
} }
@ -175,7 +175,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
return; return;
} }
int collateral = (int) (BitSquareConverter.stringToDouble2(collateralTextField.getText())); int collateral = (int) (BitSquareConverter.stringToDouble(collateralTextField.getText()));
Arbitrator arbitrator = settings.getRandomArbitrator(collateral, getAmountAsBI()); Arbitrator arbitrator = settings.getRandomArbitrator(collateral, getAmountAsBI());
if (arbitrator == null) if (arbitrator == null)
{ {
@ -189,7 +189,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
{ {
offer = new Offer(user.getMessagePubKeyAsHex(), offer = new Offer(user.getMessagePubKeyAsHex(),
direction, direction,
BitSquareConverter.stringToDouble2(priceTextField.getText()), BitSquareConverter.stringToDouble(priceTextField.getText()),
BtcFormatter.stringValueToSatoshis(amountTextField.getText()), BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
BtcFormatter.stringValueToSatoshis(minAmountTextField.getText()), BtcFormatter.stringValueToSatoshis(minAmountTextField.getText()),
user.getCurrentBankAccount().getBankAccountType(), user.getCurrentBankAccount().getBankAccountType(),
@ -202,7 +202,9 @@ public class CreateOfferController implements Initializable, ChildController, Hi
settings.getAcceptedLanguageLocales()); settings.getAcceptedLanguageLocales());
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() try
{
walletFacade.payCreateOfferFee(offer.getId(), new FutureCallback<Transaction>()
{ {
@Override @Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction) public void onSuccess(@javax.annotation.Nullable Transaction transaction)
@ -231,10 +233,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t); Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t);
placeOfferButton.setDisable(false); placeOfferButton.setDisable(false);
} }
}; });
try
{
walletFacade.payCreateOfferFee(offer.getId(), callback);
placeOfferButton.setDisable(true); placeOfferButton.setDisable(true);
} catch (InsufficientMoneyException e1) } catch (InsufficientMoneyException e1)
{ {
@ -279,8 +278,8 @@ public class CreateOfferController implements Initializable, ChildController, Hi
private double getVolume() private double getVolume()
{ {
double amountAsDouble = BitSquareConverter.stringToDouble2(amountTextField.getText()); double amountAsDouble = BitSquareConverter.stringToDouble(amountTextField.getText());
double priceAsDouble = BitSquareConverter.stringToDouble2(priceTextField.getText()); double priceAsDouble = BitSquareConverter.stringToDouble(priceTextField.getText());
return amountAsDouble * priceAsDouble; return amountAsDouble * priceAsDouble;
} }
@ -293,10 +292,10 @@ public class CreateOfferController implements Initializable, ChildController, Hi
@SuppressWarnings("UnusedAssignment") @SuppressWarnings("UnusedAssignment")
private boolean inputValid() private boolean inputValid()
{ {
double priceAsDouble = BitSquareConverter.stringToDouble2(priceTextField.getText()); double priceAsDouble = BitSquareConverter.stringToDouble(priceTextField.getText());
double minAmountAsDouble = BitSquareConverter.stringToDouble2(minAmountTextField.getText()); double minAmountAsDouble = BitSquareConverter.stringToDouble(minAmountTextField.getText());
double amountAsDouble = BitSquareConverter.stringToDouble2(amountTextField.getText()); double amountAsDouble = BitSquareConverter.stringToDouble(amountTextField.getText());
double collateralAsDouble = BitSquareConverter.stringToDouble2(collateralTextField.getText()); double collateralAsDouble = BitSquareConverter.stringToDouble(collateralTextField.getText());
return priceAsDouble > 0 && return priceAsDouble > 0 &&
amountAsDouble > 0 && amountAsDouble > 0 &&

View file

@ -13,7 +13,7 @@ import io.bitsquare.gui.MainController;
import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.NavigationItem; import io.bitsquare.gui.NavigationItem;
import io.bitsquare.gui.market.createOffer.CreateOfferController; import io.bitsquare.gui.market.createOffer.CreateOfferController;
import io.bitsquare.gui.market.trade.TakerTradeController; import io.bitsquare.gui.market.trade.TakerOfferController;
import io.bitsquare.gui.popups.Popups; import io.bitsquare.gui.popups.Popups;
import io.bitsquare.gui.util.BitSquareConverter; import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter; import io.bitsquare.gui.util.BitSquareFormatter;
@ -115,7 +115,7 @@ public class OrderBookController implements Initializable, ChildController
{ {
orderBook.init(); orderBook.init();
// setup table // init table
setCountryColumnCellFactory(); setCountryColumnCellFactory();
setBankAccountTypeColumnCellFactory(); setBankAccountTypeColumnCellFactory();
setDirectionColumnCellFactory(); setDirectionColumnCellFactory();
@ -223,16 +223,16 @@ public class OrderBookController implements Initializable, ChildController
Action response = Popups.openErrorPopup("Registration fee not confirmed yet", "The registration fee transaction has not been confirmed yet in the blockchain. Please wait until it has at least 1 confirmation."); Action response = Popups.openErrorPopup("Registration fee not confirmed yet", "The registration fee transaction has not been confirmed yet in the blockchain. Please wait until it has at least 1 confirmation.");
if (response == Dialog.Actions.OK) if (response == Dialog.Actions.OK)
{ {
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS); MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
} }
} }
} }
else else
{ {
Action response = Popups.openErrorPopup("Missing registration fee", "You have not funded the full registration fee of " + BtcFormatter.satoshiToString(FeePolicy.ACCOUNT_REGISTRATION_FEE_depr) + " BTC."); Action response = Popups.openErrorPopup("Missing registration fee", "You have not funded the full registration fee of " + BtcFormatter.formatSatoshis(FeePolicy.ACCOUNT_REGISTRATION_FEE) + " BTC.");
if (response == Dialog.Actions.OK) if (response == Dialog.Actions.OK)
{ {
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS); MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
} }
} }
} }
@ -255,11 +255,11 @@ public class OrderBookController implements Initializable, ChildController
Action registrationMissingAction = Popups.openRegistrationMissingPopup("Not registered yet", "Please follow these steps:", "You need to register before you can place an offer.", commandLinks, selectedIndex); Action registrationMissingAction = Popups.openRegistrationMissingPopup("Not registered yet", "Please follow these steps:", "You need to register before you can place an offer.", commandLinks, selectedIndex);
if (registrationMissingAction == settingsCommandLink) if (registrationMissingAction == settingsCommandLink)
{ {
MainController.INSTANCE().navigateToView(NavigationItem.SETTINGS); MainController.GET_INSTANCE().navigateToView(NavigationItem.SETTINGS);
} }
else if (registrationMissingAction == depositFeeCommandLink) else if (registrationMissingAction == depositFeeCommandLink)
{ {
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS); MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
} }
else if (registrationMissingAction == sendRegistrationCommandLink) else if (registrationMissingAction == sendRegistrationCommandLink)
{ {
@ -315,7 +315,7 @@ public class OrderBookController implements Initializable, ChildController
Action response = Popups.openErrorPopup("No funds for a trade", "You have to add some funds before you create a new offer."); Action response = Popups.openErrorPopup("No funds for a trade", "You have to add some funds before you create a new offer.");
if (response == Dialog.Actions.OK) if (response == Dialog.Actions.OK)
{ {
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS); MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
} }
} }
} }
@ -329,7 +329,7 @@ public class OrderBookController implements Initializable, ChildController
{ {
if (isRegistered()) if (isRegistered())
{ {
TakerTradeController takerTradeController = (TakerTradeController) navigationController.navigateToView(NavigationItem.TAKE_OFFER); TakerOfferController takerOfferController = (TakerOfferController) navigationController.navigateToView(NavigationItem.TAKE_OFFER);
BigInteger requestedAmount; BigInteger requestedAmount;
if (!"".equals(amount.getText())) if (!"".equals(amount.getText()))
@ -337,8 +337,8 @@ public class OrderBookController implements Initializable, ChildController
else else
requestedAmount = offer.getAmount(); requestedAmount = offer.getAmount();
if (takerTradeController != null) if (takerOfferController != null)
takerTradeController.initWithData(offer, requestedAmount); takerOfferController.initWithData(offer, requestedAmount);
} }
else else
{ {
@ -541,7 +541,7 @@ public class OrderBookController implements Initializable, ChildController
} catch (ParseException e) } catch (ParseException e)
{ {
amount.setText(oldValue); amount.setText(oldValue);
d = BitSquareConverter.stringToDouble2(oldValue); d = BitSquareConverter.stringToDouble(oldValue);
} }
} }
return d; return d;

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import io.bitsquare.gui.components.ValidatedTextField?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
@ -8,7 +9,7 @@
<Accordion fx:id="accordion" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="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> <panes>
<TitledPane fx:id="profileTitledPane" text="Offer details"> <TitledPane fx:id="takeOfferTitledPane" text="Offer details">
<ScrollPane fitToWidth="true"> <ScrollPane fitToWidth="true">
<GridPane hgap="5.0" vgap="5.0"> <GridPane hgap="5.0" vgap="5.0">
<padding> <padding>
@ -18,43 +19,41 @@
<Label text="Take offer:" id="headline-label"/> <Label text="Take offer:" id="headline-label"/>
<Label text="Amount (BTC):" GridPane.rowIndex="1"/> <Label text="Amount (BTC):" GridPane.rowIndex="1"/>
<TextField fx:id="amountTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/> <ValidatedTextField fx:id="amountTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label text="Price (EUR/BTC):" GridPane.rowIndex="2"/> <Label text="Price (EUR/BTC):" GridPane.rowIndex="2"/>
<TextField fx:id="priceTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/> <TextField fx:id="priceTextField" editable="false" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="Volume (EUR):" GridPane.rowIndex="3"/> <Label text="Volume (EUR):" GridPane.rowIndex="3"/>
<TextField fx:id="volumeTextField" GridPane.rowIndex="3" GridPane.columnIndex="1"/> <TextField fx:id="volumeTextField" editable="false" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<Label text="Collateral (BTC):" GridPane.rowIndex="4"/> <Label text="Collateral (BTC):" GridPane.rowIndex="4"/>
<TextField fx:id="collateralTextField" GridPane.rowIndex="4" GridPane.columnIndex="1"/> <TextField fx:id="collateralTextField" editable="false" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<Label text="Fee (BTC):" GridPane.rowIndex="5"/> <Label text="Fee (BTC):" GridPane.rowIndex="5"/>
<TextField fx:id="feeTextField" GridPane.rowIndex="5" GridPane.columnIndex="1"/> <TextField fx:id="feeTextField" editable="false" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<Label text="Total (BTC):" GridPane.rowIndex="6"/> <Label text="Total (BTC):" GridPane.rowIndex="6"/>
<TextField fx:id="totalTextField" GridPane.rowIndex="6" GridPane.columnIndex="1"/> <TextField fx:id="totalTextField" editable="false" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Button text="Take offer and pay" onAction="#onTakeOffer" defaultButton="true" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<Button fx:id="takeOfferButton" text="Take offer and pay" onAction="#onTakeOffer" defaultButton="true" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<Label text="Offer details:" id="headline-label" GridPane.rowIndex="8"/> <Label text="Offer details:" id="headline-label" GridPane.rowIndex="8"/>
<Label text="Bank account type:" GridPane.rowIndex="9"/> <Label text="Bank account type:" GridPane.rowIndex="9"/>
<TextField fx:id="bankAccountTypeTextField" GridPane.rowIndex="9" GridPane.columnIndex="1"/> <TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.rowIndex="9" GridPane.columnIndex="1"/>
<Label text="Country:" GridPane.rowIndex="10"/> <Label text="Country:" GridPane.rowIndex="10"/>
<TextField fx:id="countryTextField" GridPane.rowIndex="10" GridPane.columnIndex="1"/> <TextField fx:id="countryTextField" editable="false" GridPane.rowIndex="10" GridPane.columnIndex="1"/>
<Label text="Arbitrators:" GridPane.rowIndex="11"/> <Label text="Arbitrators:" GridPane.rowIndex="11"/>
<TextField fx:id="arbitratorsTextField" GridPane.rowIndex="11" GridPane.columnIndex="1"/> <TextField fx:id="arbitratorsTextField" editable="false" GridPane.rowIndex="11" GridPane.columnIndex="1"/>
<Label text="Supported languages:" GridPane.rowIndex="12"/> <Label text="Supported languages:" GridPane.rowIndex="12"/>
<TextField fx:id="supportedLanguagesTextField" GridPane.rowIndex="12" GridPane.columnIndex="1"/> <TextField fx:id="supportedLanguagesTextField" editable="false" GridPane.rowIndex="12" GridPane.columnIndex="1"/>
<Label text="Supported countries:" GridPane.rowIndex="13"/> <Label text="Supported countries:" GridPane.rowIndex="13"/>
<TextField fx:id="supportedCountriesTextField" GridPane.rowIndex="13" GridPane.columnIndex="1"/> <TextField fx:id="supportedCountriesTextField" editable="false" GridPane.rowIndex="13" GridPane.columnIndex="1"/>
<columnConstraints> <columnConstraints>
@ -63,30 +62,110 @@
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints> </rowConstraints>
</GridPane> </GridPane>
</ScrollPane> </ScrollPane>
</TitledPane> </TitledPane>
<TitledPane fx:id="payCollateralTitledPane" text="Wait for bank transfer"> <TitledPane fx:id="waitBankTxTitledPane" text="Wait for bank transfer">
<GridPane hgap="5.0" vgap="5.0">
<padding>
<Insets left="10" right="10" top="10" bottom="10"/>
</padding>
<Label fx:id="headLineLabel" text="Deposit transaction published" id="headline-label"/>
<Label text="Status information:" GridPane.rowIndex="1" GridPane.valignment="TOP"/>
<Label fx:id="infoLabel" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.valignment="TOP"/>
<Label text="Deposit transaction ID:" GridPane.rowIndex="2"/>
<TextField fx:id="depositTxIdTextField" editable="false" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Button fx:id="receivedFiatButton" text="I have received the money at my bank account" onAction="#onReceivedFiat" defaultButton="true" disable="true" GridPane.rowIndex="3"
GridPane.columnIndex="1"/>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="NEVER" minWidth="10.0"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
</GridPane>
</TitledPane> </TitledPane>
<TitledPane fx:id="payCollateralTitledPane" text="Summary"> <TitledPane fx:id="summaryTitledPane" text="Summary">
<GridPane hgap="5.0" vgap="5.0">
<padding>
<Insets left="10" right="10" top="10" bottom="10"/>
</padding>
<Label text="Trade completed" id="headline-label"/>
<Label text="Summary:" GridPane.rowIndex="1"/>
<Label text="You have sold (BTC):" GridPane.rowIndex="2"/>
<ValidatedTextField fx:id="summaryPaidTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="You have received (EUR):" GridPane.rowIndex="3"/>
<TextField fx:id="summaryReceivedTextField" editable="false" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<Label text="Details:" GridPane.rowIndex="4"/>
<Label text="Total fees (take offer fee + tx fee):" GridPane.rowIndex="5"/>
<TextField fx:id="summaryFeesTextField" editable="false" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<Label text="Refunded collateral (BTC):" GridPane.rowIndex="6"/>
<TextField fx:id="summaryCollateralTextField" editable="false" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Label text="Deposit transaction ID:" GridPane.rowIndex="7"/>
<TextField fx:id="summaryDepositTxIdTextField" editable="false" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<Label text="Payout transaction ID:" GridPane.rowIndex="8"/>
<TextField fx:id="summaryPayoutTxIdTextField" editable="false" GridPane.rowIndex="8" GridPane.columnIndex="1"/>
<Button text="Close" onAction="#onClose" defaultButton="true" GridPane.rowIndex="9" GridPane.columnIndex="1"/>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
</GridPane>
</TitledPane> </TitledPane>
</panes> </panes>

View file

@ -1,32 +1,30 @@
package io.bitsquare.gui.market.trade; package io.bitsquare.gui.market.trade;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.bank.BankAccountType; import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.BtcFormatter; import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.currency.Bitcoin;
import io.bitsquare.currency.Fiat;
import io.bitsquare.gui.ChildController; import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController; import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.NavigationItem;
import io.bitsquare.gui.components.ValidatedTextField;
import io.bitsquare.gui.popups.Popups;
import io.bitsquare.gui.util.BitSquareConverter; import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter; import io.bitsquare.gui.util.BitSquareFormatter;
import io.bitsquare.locale.CountryUtil; import io.bitsquare.gui.util.BitSquareValidator;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.msg.MessageFacade; import io.bitsquare.msg.MessageFacade;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer; import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.Trading; import io.bitsquare.trade.Trading;
import io.bitsquare.user.Arbitrator; import io.bitsquare.trade.payment.taker.TakerAsSellerProtocolListener;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URL; import java.net.URL;
import java.util.Currency;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Accordion; import javafx.scene.control.*;
import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane;
import javafx.scene.control.TitledPane;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -38,18 +36,29 @@ public class TakerOfferController implements Initializable, ChildController
private final Trading trading; private final Trading trading;
private final WalletFacade walletFacade; private final WalletFacade walletFacade;
private final MessageFacade messageFacade; private final MessageFacade messageFacade;
private NavigationController navigationController; private NavigationController navigationController;
private Offer offer; private Offer offer;
private Bitcoin requestedAmount; private BigInteger requestedAmount;
private String tradeId;
private String depositTxId;
@FXML
private AnchorPane rootContainer;
@FXML @FXML
private Accordion accordion; private Accordion accordion;
@FXML @FXML
private TitledPane profileTitledPane; private TitledPane takeOfferTitledPane, waitBankTxTitledPane, summaryTitledPane;
@FXML @FXML
private TextField amountTextField, priceTextField, volumeTextField, collateralTextField, feeTextField, totalTextField, bankAccountTypeTextField, countryTextField, private ValidatedTextField amountTextField;
arbitratorsTextField, supportedLanguagesTextField, supportedCountriesTextField; @FXML
private TextField priceTextField, volumeTextField, collateralTextField, feeTextField, totalTextField, bankAccountTypeTextField, countryTextField,
arbitratorsTextField, supportedLanguagesTextField, supportedCountriesTextField, depositTxIdTextField, summaryPaidTextField, summaryReceivedTextField, summaryFeesTextField,
summaryCollateralTextField, summaryDepositTxIdTextField, summaryPayoutTxIdTextField;
@FXML
private Label infoLabel, headLineLabel;
@FXML
private Button takeOfferButton, receivedFiatButton;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
@ -61,23 +70,6 @@ public class TakerOfferController implements Initializable, ChildController
this.trading = trading; this.trading = trading;
this.walletFacade = walletFacade; this.walletFacade = walletFacade;
this.messageFacade = messageFacade; this.messageFacade = messageFacade;
Offer offer = new Offer("m",
Direction.BUY,
111,
new BigInteger("100000000"),
new BigInteger("10000000"),
BankAccountType.OK_PAY,
Currency.getInstance("EUR"),
CountryUtil.getDefaultCountry(),
"baid",
new Arbitrator(),
10,
CountryUtil.getAllCountriesFor(CountryUtil.getAllRegions().get(0)),
LanguageUtil.getAllLanguageLocales());
initWithData(offer, new Bitcoin("50000000"));
} }
@ -85,10 +77,13 @@ public class TakerOfferController implements Initializable, ChildController
// Public methods // Public methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void initWithData(Offer offer, Bitcoin requestedAmount) public void initWithData(Offer offer, BigInteger requestedAmount)
{ {
this.offer = offer; this.offer = offer;
this.requestedAmount = requestedAmount.isZero() ? new Bitcoin(offer.getAmount()) : requestedAmount; this.requestedAmount = requestedAmount.compareTo(BigInteger.ZERO) == 0 ? offer.getAmount() : requestedAmount;
if (amountTextField != null)
applyData();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -98,17 +93,18 @@ public class TakerOfferController implements Initializable, ChildController
@Override @Override
public void initialize(URL url, ResourceBundle rb) public void initialize(URL url, ResourceBundle rb)
{ {
accordion.setExpandedPane(profileTitledPane); accordion.setExpandedPane(takeOfferTitledPane);
}
if (offer != null && requestedAmount != null) public void applyData()
{ {
amountTextField.setText(requestedAmount.getFormattedValue()); amountTextField.setText(BtcFormatter.formatSatoshis(requestedAmount));
amountTextField.setPromptText(new Bitcoin(offer.getMinAmount()).getFormattedValue() + " - " + new Bitcoin(offer.getAmount()).getFormattedValue()); amountTextField.setPromptText(BtcFormatter.formatSatoshis(offer.getMinAmount()) + " - " + BtcFormatter.formatSatoshis(offer.getAmount()));
priceTextField.setText(new Fiat(offer.getPrice()).getFormattedValue()); priceTextField.setText(BitSquareFormatter.formatPrice(offer.getPrice()));
applyVolume(); applyVolume();
applyCollateral(); applyCollateral();
applyTotal(); applyTotal();
feeTextField.setText(getFee().getFormattedValue()); feeTextField.setText(BtcFormatter.formatSatoshis(getFee()));
totalTextField.setText(getFormattedTotal()); totalTextField.setText(getFormattedTotal());
bankAccountTypeTextField.setText(offer.getBankAccountType().toString()); bankAccountTypeTextField.setText(offer.getBankAccountType().toString());
@ -127,7 +123,6 @@ public class TakerOfferController implements Initializable, ChildController
}); });
} }
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -145,19 +140,103 @@ public class TakerOfferController implements Initializable, ChildController
{ {
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// GUI handlers // GUI handlers
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@FXML
public void onTakeOffer() public void onTakeOffer()
{ {
AddressEntry addressEntry = walletFacade.getAddressInfoByTradeID(offer.getId());
BigInteger amount = BtcFormatter.stringValueToSatoshis(amountTextField.getText());
// TODO more validation (fee payment, blacklist,...)
if (amountTextField.isInvalid())
{
Popups.openErrorPopup("Invalid input", "The requested amount you entered is not a valid amount.");
}
else if (BitSquareValidator.tradeAmountOutOfRange(amount, offer))
{
Popups.openErrorPopup("Invalid input", "The requested amount you entered is outside of the range of the offered amount.");
}
else if (addressEntry == null || getTotal().compareTo(walletFacade.getBalanceForAddress(addressEntry.getAddress())) > 0)
{
Popups.openErrorPopup("Insufficient money", "You don't have enough funds for that trade.");
}
else if (trading.isOfferAlreadyInTrades(offer))
{
Popups.openErrorPopup("Offer previously accepted", "You have that offer already taken. Open the offer section to find that trade.");
}
else
{
takeOfferButton.setDisable(true);
amountTextField.setEditable(false);
trading.takeOffer(amount, offer, new TakerAsSellerProtocolListener()
{
@Override
public void onDepositTxPublished(String depositTxId)
{
setDepositTxId(depositTxId);
accordion.setExpandedPane(waitBankTxTitledPane);
infoLabel.setText("Deposit transaction published by offerer.\n" +
"As soon as the offerer starts the \n" +
"Bank transfer, you will get informed.");
depositTxIdTextField.setText(depositTxId);
} }
@Override
public void onBankTransferInited(String tradeId)
{
setTradeId(tradeId);
headLineLabel.setText("Bank transfer inited");
infoLabel.setText("Check your bank account and continue \n" +
"when you have received the money.");
receivedFiatButton.setDisable(false);
}
@Override
public void onTradeCompleted(Trade trade, String payoutTxId)
{
accordion.setExpandedPane(summaryTitledPane);
summaryPaidTextField.setText(BtcFormatter.formatSatoshis(trade.getTradeAmount()));
summaryReceivedTextField.setText(BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount())));
summaryFeesTextField.setText(BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)));
summaryCollateralTextField.setText(BtcFormatter.formatSatoshis(trade.getCollateralAmount()));
summaryDepositTxIdTextField.setText(depositTxId);
summaryPayoutTxIdTextField.setText(payoutTxId);
}
}, (task) -> {
//log.trace(task.toString());
}, throwable -> {
log.error(throwable.toString());
}
);
}
}
@FXML
public void onReceivedFiat()
{
trading.releaseBTC(tradeId);
}
@FXML
public void onClose()
{
TabPane tabPane = ((TabPane) (rootContainer.getParent().getParent()));
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
navigationController.navigateToView(NavigationItem.ORDER_BOOK);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Private methods // Private methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void applyCollateral() private void applyCollateral()
{ {
collateralTextField.setText(getFormattedCollateral()); collateralTextField.setText(getFormattedCollateral());
@ -181,18 +260,23 @@ public class TakerOfferController implements Initializable, ChildController
private String getFormattedTotal() private String getFormattedTotal()
{ {
return BitSquareFormatter.formatVolume(getTotal()); return BitSquareFormatter.formatVolume(getTotal().doubleValue());
} }
private String getFormattedCollateral() private String getFormattedCollateral()
{ {
return BtcFormatter.satoshiToString(getCollateralInSatoshis()); return BtcFormatter.formatSatoshis(getCollateralInSatoshis());
} }
// values // values
private double getAmountAsDouble() private double getAmountAsDouble()
{ {
return BitSquareConverter.stringToDouble2(amountTextField.getText()); return BitSquareConverter.stringToDouble(amountTextField.getText());
}
private BigInteger getAmountInSatoshis()
{
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
} }
private double getVolume() private double getVolume()
@ -200,22 +284,32 @@ public class TakerOfferController implements Initializable, ChildController
return offer.getPrice() * getAmountAsDouble(); return offer.getPrice() * getAmountAsDouble();
} }
private Bitcoin getFee() private BigInteger getFee()
{ {
return FeePolicy.TAKE_OFFER_FEE.addBitcoin(FeePolicy.TX_FEE); return FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE);
} }
private double getTotal() private BigInteger getTotal()
{ {
return getFee().doubleValue() + getVolume(); return getFee().add(getAmountInSatoshis()).add(getCollateralInSatoshis());
} }
private BigInteger getCollateralInSatoshis() private BigInteger getCollateralInSatoshis()
{ {
double amount = BitSquareConverter.stringToDouble2(amountTextField.getText()); double amount = BitSquareConverter.stringToDouble(amountTextField.getText());
double resultDouble = amount * (double) offer.getCollateral() / 100.0; double resultDouble = amount * (double) offer.getCollateral() / 100.0;
return BtcFormatter.doubleValueToSatoshis(resultDouble); return BtcFormatter.doubleValueToSatoshis(resultDouble);
} }
public void setTradeId(String tradeId)
{
this.tradeId = tradeId;
}
public void setDepositTxId(String depositTxId)
{
this.depositTxId = depositTxId;
}
} }

View file

@ -17,13 +17,10 @@ import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter; import io.bitsquare.gui.util.BitSquareFormatter;
import io.bitsquare.gui.util.FormBuilder; import io.bitsquare.gui.util.FormBuilder;
import io.bitsquare.msg.MessageFacade; import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.trade.Direction; import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer; import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade; import io.bitsquare.trade.Trade;
import io.bitsquare.trade.Trading; import io.bitsquare.trade.Trading;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocolListener;
import io.bitsquare.util.Utilities; import io.bitsquare.util.Utilities;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URL; import java.net.URL;
@ -149,21 +146,21 @@ public class TakerTradeController implements Initializable, ChildController
row = -1; row = -1;
FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row); FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row);
amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.satoshiToString(requestedAmount), ++row, true, true); amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.formatSatoshis(requestedAmount), ++row, true, true);
amountTextField.textProperty().addListener(e -> { amountTextField.textProperty().addListener(e -> {
applyVolume(); applyVolume();
applyCollateral(); applyCollateral();
totalToPayLabel.setText(getTotalToPayAsString()); totalToPayLabel.setText(getTotalToPayAsString());
}); });
Label amountRangeLabel = new Label("(" + BtcFormatter.satoshiToString(offer.getMinAmount()) + " - " + BtcFormatter.satoshiToString(offer.getAmount()) + ")"); Label amountRangeLabel = new Label("(" + BtcFormatter.formatSatoshis(offer.getMinAmount()) + " - " + BtcFormatter.formatSatoshis(offer.getAmount()) + ")");
gridPane.add(amountRangeLabel, 2, row); gridPane.add(amountRangeLabel, 2, row);
FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", BitSquareFormatter.formatPrice(offer.getPrice()), ++row); FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", BitSquareFormatter.formatPrice(offer.getPrice()), ++row);
totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row); totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row);
collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row); collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row);
applyCollateral(); applyCollateral();
FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.satoshiToString(FeePolicy.TAKE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr)), ++row); FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)), ++row);
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPayAsString(), ++row); totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPayAsString(), ++row);
isOnlineTextField = FormBuilder.addTextField(gridPane, "Online status:", "Checking offerers online status...", ++row); isOnlineTextField = FormBuilder.addTextField(gridPane, "Online status:", "Checking offerers online status...", ++row);
@ -228,18 +225,18 @@ public class TakerTradeController implements Initializable, ChildController
return; return;
} }
if (trading.isOfferTradable(offer)) //if (trading.isOfferAlreadyInTrades(offer))
{ // {
trade = trading.createTrade(offer); trade = trading.createTrade(offer);
trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText())); trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
/* if (!blockChainFacade.verifyAccountRegistration(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."); Popups.openErrorPopup("Offerers account ID not valid", "Offerers registration tx is not found in blockchain or does not match the requirements.");
return; return;
} }
if (blockChainFacade.isAccountIDBlacklisted(offer.getAccountID())) if (blockChainFacade.isAccountIDBlacklisted(offer.getAccountId()))
{ {
Popups.openErrorPopup("Offerers account ID is blacklisted", "Offerers account ID is blacklisted."); Popups.openErrorPopup("Offerers account ID is blacklisted", "Offerers account ID is blacklisted.");
return; return;
@ -269,7 +266,7 @@ public class TakerTradeController implements Initializable, ChildController
progressIndicatorHolder.getChildren().addAll(progressIndicator); progressIndicatorHolder.getChildren().addAll(progressIndicator);
gridPane.add(progressIndicatorHolder, 1, row); gridPane.add(progressIndicatorHolder, 1, row);
TakerPaymentProtocol takerPaymentProtocol = trading.addTakerPaymentProtocol(trade, new TakerPaymentProtocolListener() /*TakerPaymentProtocol takerPaymentProtocol = trading.addTakerPaymentProtocol(trade, new TakerPaymentProtocolListener()
{ {
@Override @Override
public void onProgress(double progress) public void onProgress(double progress)
@ -297,6 +294,7 @@ public class TakerTradeController implements Initializable, ChildController
statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation."); statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
break; break;
} */ } */
/*
} }
@Override @Override
@ -306,13 +304,13 @@ public class TakerTradeController implements Initializable, ChildController
} }
@Override @Override
public void onDepositTxPublished(String depositTxID) public void onDepositTxPublishedMessage(String depositTxID)
{ {
buildDepositPublishedScreen(depositTxID); buildDepositPublishedScreen(depositTxID);
} }
@Override @Override
public void onBankTransferInited(TradeMessage tradeMessage) public void onBankTransferInitedMessage(TradeMessage tradeMessage)
{ {
buildBankTransferInitedScreen(tradeMessage); buildBankTransferInitedScreen(tradeMessage);
} }
@ -324,9 +322,9 @@ public class TakerTradeController implements Initializable, ChildController
} }
}); });
takerPaymentProtocol.takeOffer(); takerPaymentProtocol.takeOffer(); */
}
} }
//}
@SuppressWarnings("EmptyMethod") @SuppressWarnings("EmptyMethod")
private void updateTx(Trade trade) private void updateTx(Trade trade)
@ -347,7 +345,7 @@ public class TakerTradeController implements Initializable, ChildController
// confidenceDisplay = new ConfidenceDisplay(walletFacade.getWallet(), confirmationLabel, transaction, progressIndicator); // confidenceDisplay = new ConfidenceDisplay(walletFacade.getWallet(), confirmationLabel, transaction, progressIndicator);
} }
private void buildBankTransferInitedScreen(TradeMessage tradeMessage) private void buildBankTransferInitedScreen()
{ {
processStepBar.next(); processStepBar.next();
@ -355,13 +353,13 @@ public class TakerTradeController implements Initializable, ChildController
infoLabel.setText("Check your bank account and continue \nwhen you have received the money."); infoLabel.setText("Check your bank account and continue \nwhen you have received the money.");
gridPane.add(nextButton, 1, ++row); gridPane.add(nextButton, 1, ++row);
nextButton.setText("I have received the money at my bank"); nextButton.setText("I have received the money at my bank");
nextButton.setOnAction(e -> releaseBTC(tradeMessage)); nextButton.setOnAction(e -> releaseBTC());
} }
private void releaseBTC(TradeMessage tradeMessage) private void releaseBTC()
{ {
processStepBar.next(); processStepBar.next();
trading.releaseBTC(trade.getId(), tradeMessage); trading.releaseBTC(trade.getId());
nextButton.setText("Close"); nextButton.setText("Close");
nextButton.setOnAction(e -> close()); nextButton.setOnAction(e -> close());
@ -372,19 +370,19 @@ public class TakerTradeController implements Initializable, ChildController
String fiatReceived = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount())); String fiatReceived = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount()));
FormBuilder.addTextField(gridPane, "You have sold (BTC):", BtcFormatter.satoshiToString(trade.getTradeAmount()), ++row); FormBuilder.addTextField(gridPane, "You have sold (BTC):", BtcFormatter.formatSatoshis(trade.getTradeAmount()), ++row);
if (takerIsSelling()) if (takerIsSelling())
{ {
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):\"", fiatReceived, ++row); FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):\"", fiatReceived, ++row);
FormBuilder.addTextField(gridPane, "Total fees (take offer fee + tx fee):", BtcFormatter.satoshiToString(FeePolicy.TAKE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr)), ++row); FormBuilder.addTextField(gridPane, "Total fees (take offer fee + tx fee):", BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)), ++row);
FormBuilder.addTextField(gridPane, "Refunded collateral:", BtcFormatter.satoshiToString(trade.getCollateralAmount()), ++row); FormBuilder.addTextField(gridPane, "Refunded collateral:", BtcFormatter.formatSatoshis(trade.getCollateralAmount()), ++row);
} }
else else
{ {
//TODO //TODO
FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.satoshiToString(getCollateralInSatoshis()), ++row); FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis()), ++row);
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row); FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row);
FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.satoshiToString(offer.getAmount()), ++row); FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.formatSatoshis(offer.getAmount()), ++row);
} }
gridPane.add(nextButton, 1, ++row); gridPane.add(nextButton, 1, ++row);
@ -440,18 +438,18 @@ public class TakerTradeController implements Initializable, ChildController
private double getVolume() private double getVolume()
{ {
return offer.getPrice() * BitSquareConverter.stringToDouble2(amountTextField.getText()); return offer.getPrice() * BitSquareConverter.stringToDouble(amountTextField.getText());
} }
private String getTotalToPayAsString() private String getTotalToPayAsString()
{ {
if (takerIsSelling()) if (takerIsSelling())
{ {
return BtcFormatter.satoshiToString(getTotalToPay()); return BtcFormatter.formatSatoshis(getTotalToPay());
} }
else else
{ {
return BtcFormatter.satoshiToString(getTotalToPay()) + "\n" + return BtcFormatter.formatSatoshis(getTotalToPay()) + "\n" +
BitSquareFormatter.formatVolume(getVolume(), offer.getCurrency()); BitSquareFormatter.formatVolume(getVolume(), offer.getCurrency());
} }
} }
@ -460,22 +458,22 @@ public class TakerTradeController implements Initializable, ChildController
{ {
if (takerIsSelling()) if (takerIsSelling())
{ {
return getAmountInSatoshis().add(FeePolicy.TAKE_OFFER_FEE_depr).add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE_depr).add(getCollateralInSatoshis()); return getAmountInSatoshis().add(FeePolicy.TAKE_OFFER_FEE).add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE).add(getCollateralInSatoshis());
} }
else else
{ {
return FeePolicy.TAKE_OFFER_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE_depr).add(getCollateralInSatoshis()); return FeePolicy.TAKE_OFFER_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE).add(getCollateralInSatoshis());
} }
} }
private void applyCollateral() private void applyCollateral()
{ {
collateralTextField.setText(BtcFormatter.satoshiToString(getCollateralInSatoshis())); collateralTextField.setText(BtcFormatter.formatSatoshis(getCollateralInSatoshis()));
} }
private BigInteger getCollateralInSatoshis() private BigInteger getCollateralInSatoshis()
{ {
double amount = BitSquareConverter.stringToDouble2(amountTextField.getText()); double amount = BitSquareConverter.stringToDouble(amountTextField.getText());
double resultDouble = amount * (double) offer.getCollateral() / 100.0; double resultDouble = amount * (double) offer.getCollateral() / 100.0;
return BtcFormatter.doubleValueToSatoshis(resultDouble); return BtcFormatter.doubleValueToSatoshis(resultDouble);
} }

View file

@ -16,20 +16,38 @@ import org.slf4j.LoggerFactory;
public class OrdersController implements Initializable, ChildController, NavigationController public class OrdersController implements Initializable, ChildController, NavigationController
{ {
private static final Logger log = LoggerFactory.getLogger(OrdersController.class); private static final Logger log = LoggerFactory.getLogger(OrdersController.class);
private static int SELECTED_TAB_INDEX = -1;
private static OrdersController INSTANCE;
private final Storage storage; private final Storage storage;
@FXML @FXML
private LazyLoadingTabPane tabPane; private LazyLoadingTabPane tabPane;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private OrdersController(Storage storage) private OrdersController(Storage storage)
{ {
this.storage = storage; this.storage = storage;
INSTANCE = this;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public static OrdersController GET_INSTANCE()
{
return INSTANCE;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void setSelectedTabIndex(int index)
{
log.trace("setSelectedTabIndex " + index);
tabPane.setSelectedTabIndex(index);
storage.write(this.getClass().getName() + ".selectedTabIndex", index);
} }
@ -40,6 +58,7 @@ public class OrdersController implements Initializable, ChildController, Navigat
@Override @Override
public void initialize(URL url, ResourceBundle rb) public void initialize(URL url, ResourceBundle rb)
{ {
log.trace("initialize ");
tabPane.initialize(this, storage, NavigationItem.OFFER.getFxmlUrl(), NavigationItem.PENDING_TRADE.getFxmlUrl(), NavigationItem.CLOSED_TRADE.getFxmlUrl()); tabPane.initialize(this, storage, NavigationItem.OFFER.getFxmlUrl(), NavigationItem.PENDING_TRADE.getFxmlUrl(), NavigationItem.CLOSED_TRADE.getFxmlUrl());
} }
@ -59,6 +78,7 @@ public class OrdersController implements Initializable, ChildController, Navigat
tabPane.cleanup(); tabPane.cleanup();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: NavigationController // Interface implementation: NavigationController
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -68,7 +68,6 @@ public class OfferController implements Initializable, ChildController, Hibernat
@Override @Override
public void setNavigationController(NavigationController navigationController) public void setNavigationController(NavigationController navigationController)
{ {
log.debug("setNavigationController" + this);
} }
@Override @Override

View file

@ -35,7 +35,6 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.geometry.Pos; import javafx.geometry.Pos;
@ -165,21 +164,16 @@ public class PendingTradeController implements Initializable, ChildController, H
initCopyIcons(); initCopyIcons();
if (tradeItems.size() > 0) // select
{ Optional<PendingTradesListItem> currentTradeItemOptional = tradeItems.stream().filter((e) -> e.getTrade().getId().equals(trading.getCurrentPendingTrade().getId())).findFirst();
openTradesTable.getSelectionModel().select(0); if (currentTradeItemOptional.isPresent())
} openTradesTable.getSelectionModel().select(currentTradeItemOptional.get());
tradeItems.addListener(new ListChangeListener<PendingTradesListItem>() tradeItems.addListener((ListChangeListener<PendingTradesListItem>) change -> {
{
@Override
public void onChanged(Change<? extends PendingTradesListItem> change)
{
if (openTradesTable.getSelectionModel().getSelectedItem() == null && tradeItems.size() > 0) if (openTradesTable.getSelectionModel().getSelectedItem() == null && tradeItems.size() > 0)
{ {
openTradesTable.getSelectionModel().select(0); openTradesTable.getSelectionModel().select(0);
} }
}
}); });
} }
@ -187,13 +181,13 @@ public class PendingTradeController implements Initializable, ChildController, H
// GUI handlers // GUI handlers
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void bankTransferInited(ActionEvent actionEvent) public void bankTransferInited()
{ {
trading.onBankTransferInited(currentTrade.getId()); trading.onUIEventBankTransferInited(currentTrade.getId());
bankTransferInitedButton.setDisable(true); bankTransferInitedButton.setDisable(true);
} }
public void close(ActionEvent actionEvent) public void close()
{ {
} }
@ -328,10 +322,10 @@ public class PendingTradeController implements Initializable, ChildController, H
String fiatPayed = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount())); String fiatPayed = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount()));
bankAccountTypeTextField.setText(BtcFormatter.satoshiToString(trade.getTradeAmount())); bankAccountTypeTextField.setText(BtcFormatter.formatSatoshis(trade.getTradeAmount()));
holderNameTextField.setText(fiatPayed); holderNameTextField.setText(fiatPayed);
primaryBankAccountIDTextField.setText(BtcFormatter.satoshiToString(FeePolicy.CREATE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr))); primaryBankAccountIDTextField.setText(BtcFormatter.formatSatoshis(FeePolicy.CREATE_OFFER_FEE.add(FeePolicy.TX_FEE)));
secondaryBankAccountIDTextField.setText(BtcFormatter.satoshiToString(trade.getCollateralAmount())); secondaryBankAccountIDTextField.setText(BtcFormatter.formatSatoshis(trade.getCollateralAmount()));
holderNameCopyIcon.setVisible(false); holderNameCopyIcon.setVisible(false);
primaryBankAccountIDCopyIcon.setVisible(false); primaryBankAccountIDCopyIcon.setVisible(false);

View file

@ -14,22 +14,12 @@ public class BitSquareConverter
public static double stringToDouble(String input) public static double stringToDouble(String input)
{ {
try try
{
return stringToDouble2(input);
} catch (NumberFormatException | NullPointerException e)
{
return Double.NEGATIVE_INFINITY;
}
}
/**
* @param input String to be converted to a double. Both decimal points "." and "," are supported. Thousands separator is not supported.
* @return Returns a double value. Any invalid value throws an exception.
*/
public static double stringToDouble2(String input)
{ {
input = input.replace(",", "."); input = input.replace(",", ".");
return Double.parseDouble(input); return Double.parseDouble(input);
} catch (NumberFormatException | NullPointerException e)
{
return 0;
}
} }
} }

View file

@ -1,6 +1,8 @@
package io.bitsquare.gui.util; package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType; import io.bitsquare.bank.BankAccountType;
import io.bitsquare.trade.Offer;
import java.math.BigInteger;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.effect.BlurType; import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow; import javafx.scene.effect.DropShadow;
@ -13,6 +15,16 @@ public class BitSquareValidator
private static final Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0); private static final Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0);
private static final String invalidStyle = "-fx-border-color: red"; private static final String invalidStyle = "-fx-border-color: red";
public static boolean tradeAmountOutOfRange(BigInteger tradeAmount, Offer offer)
{
return tradeAmount.compareTo(offer.getAmount()) > 0 || tradeAmount.compareTo(offer.getMinAmount()) < 0;
}
public static boolean greaterThanZero(BigInteger value)
{
return value.compareTo(BigInteger.ZERO) > 0;
}
public static void textFieldsNotEmptyWithReset(TextField... textFields) throws ValidationException public static void textFieldsNotEmptyWithReset(TextField... textFields) throws ValidationException
{ {
resetTextFields(textFields); resetTextFields(textFields);

View file

@ -187,7 +187,7 @@ public class ConfidenceDisplay
} }
if (balanceTextField != null) if (balanceTextField != null)
balanceTextField.setText(BtcFormatter.satoshiToString(balance)); balanceTextField.setText(BtcFormatter.formatSatoshis(balance));
} }
private void updateConfidence(Transaction tx) private void updateConfidence(Transaction tx)

View file

@ -4,8 +4,14 @@ import com.google.inject.Inject;
import io.bitsquare.BitSquare; import io.bitsquare.BitSquare;
import io.bitsquare.msg.listeners.*; import io.bitsquare.msg.listeners.*;
import io.bitsquare.trade.Offer; import io.bitsquare.trade.Offer;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocol; import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol; import io.bitsquare.trade.payment.offerer.messages.*;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.payment.taker.listeners.GetPeerAddressListener;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.payment.taker.messages.RequestTakeOfferMessage;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.bitsquare.user.Arbitrator; import io.bitsquare.user.Arbitrator;
import io.bitsquare.util.DSAKeyUtil; import io.bitsquare.util.DSAKeyUtil;
import io.bitsquare.util.FileUtil; import io.bitsquare.util.FileUtil;
@ -46,9 +52,10 @@ public class MessageFacade
private final List<OrderBookListener> orderBookListeners = new ArrayList<>(); private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
private final List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>(); private final List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>();
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>(); private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
// //TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols)
private final List<TakerPaymentProtocol> takerPaymentProtocols = new ArrayList<>(); private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final List<OffererPaymentProtocol> offererPaymentProtocols = new ArrayList<>(); private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocols = new HashMap<>();
private final List<PingPeerListener> pingPeerListeners = new ArrayList<>(); private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
private final BooleanProperty isDirty = new SimpleBooleanProperty(false); private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
private Peer myPeer; private Peer myPeer;
@ -66,7 +73,7 @@ public class MessageFacade
{ {
/* try /* try
{ {
masterPeer = BootstrapMasterPeer.INSTANCE(MASTER_PEER_PORT); masterPeer = BootstrapMasterPeer.GET_INSTANCE(MASTER_PEER_PORT);
} catch (Exception e) } catch (Exception e)
{ {
if (masterPeer != null) if (masterPeer != null)
@ -97,7 +104,7 @@ public class MessageFacade
} catch (IOException e) } catch (IOException e)
{ {
shutDown(); shutDown();
log.error("Error at setup myPeerInstance" + e.getMessage()); log.error("Error at init myPeerInstance" + e.getMessage());
} }
} }
@ -323,7 +330,7 @@ public class MessageFacade
private void onGetDirtyFlag(long timeStamp) private void onGetDirtyFlag(long timeStamp)
{ {
// TODO don't get updates at first run.... // TODO don't get updates at first execute....
if (lastTimeStamp != timeStamp) if (lastTimeStamp != timeStamp)
{ {
isDirty.setValue(!isDirty.get()); isDirty.setValue(!isDirty.get());
@ -423,7 +430,8 @@ public class MessageFacade
// Find peer address // Find peer address
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(final String pubKeyAsHex, AddressLookupListener listener)
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
{ {
final Number160 location = Number160.createHash(pubKeyAsHex); final Number160 location = Number160.createHash(pubKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start(); final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
@ -435,32 +443,44 @@ public class MessageFacade
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null) if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
{ {
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject(); final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
Platform.runLater(() -> onAddressFound(peerAddress, listener)); Platform.runLater(() -> listener.onResult(peerAddress));
} }
else else
{ {
Platform.runLater(() -> onGetPeerAddressFailed(listener)); Platform.runLater(() -> listener.onFailed());
} }
} }
}); });
} }
private void onAddressFound(final PeerAddress peerAddress, AddressLookupListener listener)
{
listener.onResult(peerAddress);
}
private void onGetPeerAddressFailed(AddressLookupListener listener)
{
listener.onFailed();
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Trade process // Trade process
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void sendTradeMessage(final PeerAddress peerAddress, final TradeMessage tradeMessage, TradeMessageListener listener) public void sendTradingMessage(final PeerAddress peerAddress, 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>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (sendFuture.isSuccess())
{
Platform.runLater(() -> listener.onResult());
}
else
{
Platform.runLater(() -> listener.onFailed());
}
}
}
);
}
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, TradeMessageListener listener)
{ {
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10); final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start(); final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
@ -499,51 +519,47 @@ public class MessageFacade
private void processTradingMessage(TradeMessage tradeMessage, PeerAddress sender) private void processTradingMessage(TradeMessage tradeMessage, PeerAddress sender)
{ {
//TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols) // log.trace("processTradingMessage TradeId " + tradeMessage.getTradeId());
log.info("processTradingMessage " + tradeMessage.getType()); log.trace("processTradingMessage instance " + tradeMessage.getClass().getSimpleName());
switch (tradeMessage.getType()) log.trace("processTradingMessage instance " + tradeMessage.getClass().getName());
{ log.trace("processTradingMessage instance " + tradeMessage.getClass().getCanonicalName());
case REQUEST_TAKE_OFFER: log.trace("processTradingMessage instance " + tradeMessage.getClass().getTypeName());
// 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(tradeMessage);
break;
case PAYOUT_TX_PUBLISHED:
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
offererPaymentProtocol.onPayoutTxPublished(tradeMessage);
break;
default: if (tradeMessage instanceof RequestTakeOfferMessage)
log.info("default"); {
break; takeOfferRequestListeners.stream().forEach(e -> e.onTakeOfferRequested(tradeMessage.getTradeId(), sender));
}
else if (tradeMessage instanceof AcceptTakeOfferRequestMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onAcceptTakeOfferRequestMessage();
}
else if (tradeMessage instanceof RejectTakeOfferRequestMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onRejectTakeOfferRequestMessage();
}
else if (tradeMessage instanceof TakeOfferFeePayedMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onTakeOfferFeePayedMessage((TakeOfferFeePayedMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestTakerDepositPaymentMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onRequestTakerDepositPaymentMessage((RequestTakerDepositPaymentMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage);
}
else if (tradeMessage instanceof DepositTxPublishedMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onDepositTxPublishedMessage((DepositTxPublishedMessage) tradeMessage);
}
else if (tradeMessage instanceof BankTransferInitedMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onBankTransferInitedMessage((BankTransferInitedMessage) tradeMessage);
}
else if (tradeMessage instanceof PayoutTxPublishedMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onPayoutTxPublishedMessage((PayoutTxPublishedMessage) tradeMessage);
} }
} }
@ -616,12 +632,12 @@ public class MessageFacade
// Misc // Misc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public PublicKey getPubKey() public PublicKey getPubKey()
{ {
return keyPair.getPublic(); return keyPair.getPublic();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Event Listeners // Event Listeners
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -646,24 +662,24 @@ public class MessageFacade
takeOfferRequestListeners.remove(listener); takeOfferRequestListeners.remove(listener);
} }
public void addTakerPaymentProtocol(TakerPaymentProtocol listener) public void addTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{ {
takerPaymentProtocols.add(listener); takerPaymentProtocols.put(protocol.getId(), protocol);
} }
public void removeTakerPaymentProtocol(TakerPaymentProtocol listener) public void removeTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{ {
takerPaymentProtocols.remove(listener); takerPaymentProtocols.remove(protocol);
} }
public void addOffererPaymentProtocol(OffererPaymentProtocol listener) public void addOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{ {
offererPaymentProtocols.add(listener); offererAsBuyerProtocols.put(protocol.getId(), protocol);
} }
public void removeOffererPaymentProtocol(OffererPaymentProtocol listener) public void removeOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{ {
offererPaymentProtocols.remove(listener); offererAsBuyerProtocols.remove(protocol);
} }
public void addPingPeerListener(PingPeerListener listener) public void addPingPeerListener(PingPeerListener listener)
@ -781,5 +797,4 @@ public class MessageFacade
} */ } */
} }
} }

View file

@ -1,277 +1,6 @@
package io.bitsquare.msg; package io.bitsquare.msg;
import io.bitsquare.bank.BankAccount; public interface TradeMessage
import java.io.Serializable;
import java.math.BigInteger;
import java.util.UUID;
//TODO refactor
@SuppressWarnings("SameParameterValue")
public class TradeMessage implements Serializable
{ {
private static final long serialVersionUID = 7916445031849763995L; public String getTradeId();
private final String uid;
private final TradeMessageType type;
private final String offerUID;
private String takerMessagePubKey;
private String signedTakerDepositTxAsHex;
private String txScriptSigAsHex;
private String txConnOutAsHex;
private String contractAsJson;
private String takerContractSignature;
private String takerPayoutAddress;
private String depositTxID;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
private BigInteger tradeAmount;
private String takeOfferFeeTxID;
private String takerMultiSigPubKey;
private BankAccount bankAccount;
private String accountID;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
private long takerTxOutIndex;
private String payoutTxAsHex;
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, long offererTxOutIndex)
{
this.offerUID = offerUID;
this.type = type;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.offererPubKey = offererPubKey;
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
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,
String takerPayoutAddress,
long takerTxOutIndex,
long offererTxOutIndex)
{
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;
this.takerPayoutAddress = takerPayoutAddress;
this.takerTxOutIndex = takerTxOutIndex;
this.offererTxOutIndex = offererTxOutIndex;
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID, String depositTxAsHex)
{
this.offerUID = offerUID;
this.type = type;
this.depositTxAsHex = depositTxAsHex;
uid = UUID.randomUUID().toString();
}
// 3.10
public TradeMessage(TradeMessageType type, String offerUID,
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererPayoutAddress)
{
this.offerUID = offerUID;
this.type = type;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
this.takerPaybackAmount = takerPaybackAmount;
this.offererPayoutAddress = offererPayoutAddress;
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;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
public void setPayoutTxAsHex(String payoutTxAsHex)
{
this.payoutTxAsHex = payoutTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
} }

View file

@ -1,15 +0,0 @@
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,
PAYOUT_TX_PUBLISHED
}

View file

@ -1,9 +1,8 @@
package io.bitsquare.msg.listeners; package io.bitsquare.msg.listeners;
import io.bitsquare.msg.TradeMessage;
import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerAddress;
public interface TakeOfferRequestListener public interface TakeOfferRequestListener
{ {
void onTakeOfferRequested(TradeMessage tradeMessage, PeerAddress sender); void onTakeOfferRequested(String offerId, PeerAddress sender);
} }

View file

@ -27,7 +27,6 @@ public class Storage
@GuardedBy("lock") @GuardedBy("lock")
private Map<String, Serializable> rootMap = new HashMap<>(); private Map<String, Serializable> rootMap = new HashMap<>();
private boolean dirty;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -120,7 +119,6 @@ public class Storage
try try
{ {
lock.lock(); lock.lock();
dirty = true;
rootMap.put(key, value); rootMap.put(key, value);
saveObjectToFile((Serializable) rootMap); saveObjectToFile((Serializable) rootMap);
} finally } finally
@ -141,20 +139,23 @@ public class Storage
return read(classInstance.getClass().getName() + "." + propertyKey); return read(classInstance.getClass().getName() + "." + propertyKey);
} }
// read from local rootMap, just if not found read from disc
public Serializable read(String key) public Serializable read(String key)
{ {
try try
{ {
lock.lock(); lock.lock();
if (dirty) if (rootMap.containsKey(key))
{
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
return rootMap.get(key);
}
else
{ {
final Map<String, Serializable> map = readRootMap(); final Map<String, Serializable> map = readRootMap();
if (map != null) if (map != null)
{ {
rootMap = map; rootMap = map;
dirty = false;
}
} }
if (rootMap.containsKey(key)) if (rootMap.containsKey(key))
{ {
@ -163,9 +164,10 @@ public class Storage
} }
else else
{ {
log.warn("Object with key = " + key + " not found."); log.info("Object with key = " + key + " not found.");
return null; return null;
} }
}
} finally } finally
{ {
lock.unlock(); lock.unlock();
@ -205,7 +207,7 @@ public class Storage
} catch (FileNotFoundException e) } catch (FileNotFoundException e)
{ {
log.trace("File not found is ok for the first run."); log.trace("File not found is ok for the first execute.");
return null; return null;
} catch (ClassNotFoundException | IOException e2) } catch (ClassNotFoundException | IOException e2)
{ {

View file

@ -23,7 +23,7 @@ public class Trade implements Serializable
private Transaction depositTransaction; private Transaction depositTransaction;
private Transaction payoutTransaction; private Transaction payoutTransaction;
private State state = State.NONE; private State state = State.OPEN;
public Trade(Offer offer) public Trade(Offer offer)
{ {
@ -39,21 +39,6 @@ public class Trade implements Serializable
// Setters // Setters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return offer.getId();
}
public Offer getOffer()
{
return offer;
}
public String getTakeOfferFeeTxID()
{
return takeOfferFeeTxID;
}
public void setTakeOfferFeeTxID(String takeOfferFeeTxID) public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
{ {
this.takeOfferFeeTxID = takeOfferFeeTxID; this.takeOfferFeeTxID = takeOfferFeeTxID;
@ -80,16 +65,31 @@ public class Trade implements Serializable
contractChangedProperty.set(!contractChangedProperty.get()); contractChangedProperty.set(!contractChangedProperty.get());
} }
public String getId()
{
return offer.getId();
}
/////////////////////////////////////////////////////////////////////////////////////////// public Offer getOffer()
// Getters {
/////////////////////////////////////////////////////////////////////////////////////////// return offer;
}
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxID;
}
public String getContractAsJson() public String getContractAsJson()
{ {
return contractAsJson; return contractAsJson;
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public void setContractAsJson(String contractAsJson) public void setContractAsJson(String contractAsJson)
{ {
this.contractAsJson = contractAsJson; this.contractAsJson = contractAsJson;
@ -122,7 +122,6 @@ public class Trade implements Serializable
payoutTxChangedProperty.set(!payoutTxChangedProperty.get()); payoutTxChangedProperty.set(!payoutTxChangedProperty.get());
} }
public State getState() public State getState()
{ {
return state; return state;
@ -134,7 +133,6 @@ public class Trade implements Serializable
stateChangedProperty.set(state.toString()); stateChangedProperty.set(state.toString());
} }
public SimpleBooleanProperty getDepositTxChangedProperty() public SimpleBooleanProperty getDepositTxChangedProperty()
{ {
return depositTxChangedProperty; return depositTxChangedProperty;
@ -186,7 +184,7 @@ public class Trade implements Serializable
public static enum State public static enum State
{ {
NONE, OPEN,
ACCEPTED, ACCEPTED,
COMPLETED COMPLETED
} }

View file

@ -7,16 +7,18 @@ import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade; import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.gui.popups.Popups;
import io.bitsquare.msg.MessageFacade; import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocol; import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocolListener; import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocolListener;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol; import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocolListener; import io.bitsquare.trade.payment.taker.TakerAsSellerProtocolListener;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
@ -32,8 +34,8 @@ import org.slf4j.LoggerFactory;
public class Trading public class Trading
{ {
private static final Logger log = LoggerFactory.getLogger(Trading.class); private static final Logger log = LoggerFactory.getLogger(Trading.class);
private final Map<String, TakerPaymentProtocol> takerPaymentProtocols = new HashMap<>(); private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererPaymentProtocol> offererPaymentProtocols = new HashMap<>(); private final Map<String, OffererAsBuyerProtocol> offererPaymentProtocols = new HashMap<>();
private final String storageKey; private final String storageKey;
private final User user; private final User user;
@ -44,9 +46,10 @@ public class Trading
private final CryptoFacade cryptoFacade; private final CryptoFacade cryptoFacade;
private final StringProperty newTradeProperty = new SimpleStringProperty(); private final StringProperty newTradeProperty = new SimpleStringProperty();
private Map<String, Offer> myOffers = new HashMap<>(); private Map<String, Offer> offers = new HashMap<>();
private Map<String, Trade> trades = new HashMap<>(); private Map<String, Trade> trades = new HashMap<>();
private Trade currentPendingTrade;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -73,15 +76,15 @@ public class Trading
Object offersObject = storage.read(storageKey + ".offers"); Object offersObject = storage.read(storageKey + ".offers");
if (offersObject instanceof HashMap) if (offersObject instanceof HashMap)
myOffers = (Map<String, Offer>) offersObject; offers = (Map<String, Offer>) offersObject;
Object tradesObject = storage.read(storageKey + ".trades"); Object tradesObject = storage.read(storageKey + ".trades");
if (tradesObject instanceof HashMap) if (tradesObject instanceof HashMap)
trades = (Map<String, Trade>) tradesObject; trades = (Map<String, Trade>) tradesObject;
messageFacade.addTakeOfferRequestListener((offerId, sender) -> createOffererAsBuyerProtocol(offerId, sender));
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods // Public Methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -92,15 +95,15 @@ public class Trading
private void saveOffers() private void saveOffers()
{ {
storage.write(storageKey + ".offers", myOffers); storage.write(storageKey + ".offers", offers);
} }
public void addOffer(Offer offer) throws IOException public void addOffer(Offer offer) throws IOException
{ {
if (myOffers.containsKey(offer.getId())) if (offers.containsKey(offer.getId()))
throw new IllegalStateException("offers contains already a offer with the ID " + offer.getId()); throw new IllegalStateException("offers contains already a offer with the ID " + offer.getId());
myOffers.put(offer.getId(), offer); offers.put(offer.getId(), offer);
saveOffers(); saveOffers();
messageFacade.addOffer(offer); messageFacade.addOffer(offer);
@ -108,17 +111,12 @@ public class Trading
public void removeOffer(Offer offer) throws IOException public void removeOffer(Offer offer) throws IOException
{ {
myOffers.remove(offer.getId()); offers.remove(offer.getId());
saveOffers(); saveOffers();
messageFacade.removeOffer(offer); messageFacade.removeOffer(offer);
} }
public boolean isOfferTradable(Offer offer)
{
return !trades.containsKey(offer.getId());
}
public Trade createTrade(Offer offer) public Trade createTrade(Offer offer)
{ {
@ -144,85 +142,88 @@ public class Trading
return this.newTradeProperty; return this.newTradeProperty;
} }
public Trade takeOffer(BigInteger amount, Offer offer, TakerAsSellerProtocolListener listener, WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
public TakerPaymentProtocol addTakerPaymentProtocol(Trade trade, TakerPaymentProtocolListener listener)
{
TakerPaymentProtocol takerPaymentProtocol = new TakerPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
takerPaymentProtocols.put(trade.getId(), takerPaymentProtocol);
return takerPaymentProtocol;
}
OffererPaymentProtocol addOffererPaymentProtocol(Trade trade, OffererPaymentProtocolListener listener)
{
OffererPaymentProtocol offererPaymentProtocol = new OffererPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
offererPaymentProtocols.put(trade.getId(), offererPaymentProtocol);
return offererPaymentProtocol;
}
public void createOffererPaymentProtocol(TradeMessage tradeMessage, PeerAddress sender)
{
Offer offer = myOffers.get(tradeMessage.getOfferUID());
if (isOfferTradable(offer))
{ {
Trade trade = createTrade(offer); Trade trade = createTrade(offer);
OffererPaymentProtocol offererPaymentProtocol = addOffererPaymentProtocol(trade, new OffererPaymentProtocolListener() trade.setTradeAmount(amount);
{
@Override TakerAsSellerProtocol takerPaymentProtocol = new TakerAsSellerProtocol(trade, listener, resultHandler, faultHandler, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
public void onProgress(double progress) takerPaymentProtocols.put(trade.getId(), takerPaymentProtocol);
{
//log.debug("onProgress " + progress); return trade;
} }
@Override
public void onFailure(String failureMessage)
{
log.warn(failureMessage);
}
public void createOffererAsBuyerProtocol(String offerId, PeerAddress sender)
{
log.trace("createOffererAsBuyerProtocol offerId = " + offerId);
Offer offer = offers.get(offerId);
if (offer != null && offers.containsKey(offer.getId()))
{
offers.remove(offer);
currentPendingTrade = createTrade(offer);
OffererAsBuyerProtocolListener listener = new OffererAsBuyerProtocolListener()
{
@Override @Override
public void onDepositTxPublished(String depositTxID) public void onDepositTxPublished(String depositTxID)
{ {
log.debug("trading onDepositTxPublished " + depositTxID); log.trace("trading onDepositTxPublishedMessage " + depositTxID);
} }
@Override @Override
public void onDepositTxConfirmedUpdate(TransactionConfidence confidence) public void onDepositTxConfirmedUpdate(TransactionConfidence confidence)
{ {
log.debug("trading onDepositTxConfirmedUpdate"); log.trace("trading onDepositTxConfirmedUpdate");
} }
@Override @Override
public void onPayoutTxPublished(String payoutTxAsHex) public void onPayoutTxPublished(String payoutTxAsHex)
{ {
Transaction payoutTx = new Transaction(walletFacade.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex)); Transaction payoutTx = new Transaction(walletFacade.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex));
trade.setPayoutTransaction(payoutTx); currentPendingTrade.setPayoutTransaction(payoutTx);
trade.setState(Trade.State.COMPLETED); currentPendingTrade.setState(Trade.State.COMPLETED);
log.debug("trading onPayoutTxPublished"); log.debug("trading onPayoutTxPublishedMessage");
} }
@Override @Override
public void onDepositTxConfirmedInBlockchain() public void onDepositTxConfirmedInBlockchain()
{ {
log.debug("trading onDepositTxConfirmedInBlockchain"); log.trace("trading onDepositTxConfirmedInBlockchain");
} }
}); };
// the handler was not called there because the object was not created when the event occurred (and therefor no listener) WorkerResultHandler resultHandler = new WorkerResultHandler()
// will probably created earlier, so let it for the moment like that.... {
offererPaymentProtocol.onTakeOfferRequested(sender); @Override
public void onResult(Worker worker)
{
//log.trace("onResult " + worker.toString());
}
};
WorkerFaultHandler faultHandler = new WorkerFaultHandler()
{
@Override
public void onFault(Throwable throwable)
{
log.error("onFault " + throwable);
}
};
OffererAsBuyerProtocol offererAsBuyerProtocol = new OffererAsBuyerProtocol(currentPendingTrade, sender, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user, resultHandler, faultHandler, listener);
offererPaymentProtocols.put(currentPendingTrade.getId(), offererAsBuyerProtocol);
} }
else else
{ {
Popups.openWarningPopup("Offer already taken", "You have that offer already taken. Find that trade under Orders/Open or Pending."); log.warn("Incoming offer take request does not match with any saved offer. We ignore that request.");
} }
} }
public void onBankTransferInited(String tradeUID) public void onUIEventBankTransferInited(String tradeUID)
{ {
offererPaymentProtocols.get(tradeUID).bankTransferInited(); offererPaymentProtocols.get(tradeUID).onUIEventBankTransferInited();
} }
@ -232,9 +233,9 @@ public class Trading
// 6 // 6
public void releaseBTC(String tradeUID, TradeMessage tradeMessage) public void releaseBTC(String tradeUID)
{ {
takerPaymentProtocols.get(tradeUID).releaseBTC(tradeMessage); takerPaymentProtocols.get(tradeUID).onUIEventFiatReceived();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -250,11 +251,22 @@ public class Trading
public Map<String, Offer> getOffers() public Map<String, Offer> getOffers()
{ {
return myOffers; return offers;
} }
public Offer getOffer(String offerId) public Offer getOffer(String offerId)
{ {
return myOffers.get(offerId); return offers.get(offerId);
}
public boolean isOfferAlreadyInTrades(Offer offer)
{
return trades.containsKey(offer.getId());
}
public Trade getCurrentPendingTrade()
{
return currentPendingTrade;
} }
} }

View file

@ -0,0 +1,26 @@
package io.bitsquare.trade.payment;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.nucleo.scheduler.model.PropertyProviderModel;
import net.tomp2p.peers.PeerAddress;
public class PaymentModel extends PropertyProviderModel
{
public final MessageFacade messageFacade;
public final Offer offer;
public PeerAddress peerAddress;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public PaymentModel(MessageFacade messageFacade, Trade trade)
{
this.messageFacade = messageFacade;
this.offer = trade.getOffer();
}
}

View file

@ -1,88 +1,320 @@
package io.bitsquare.trade.payment.offerer; package io.bitsquare.trade.payment.offerer;
import com.google.bitcoin.core.*;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.bank.BankAccount; import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade; import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade; 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.trade.Trade;
import io.bitsquare.trade.payment.offerer.tasks.*;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import io.bitsquare.util.Utilities; import io.nucleo.scheduler.SequenceScheduler;
import java.math.BigInteger; import io.nucleo.scheduler.worker.Worker;
import javafx.util.Pair; import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.util.ArrayList;
import java.util.List;
import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern //TODO refactor to process based pattern
public class OffererPaymentProtocol public class OffererAsBuyerProtocol
{ {
private static final Logger log = LoggerFactory.getLogger(OffererPaymentProtocol.class); private static final Logger log = LoggerFactory.getLogger(OffererAsBuyerProtocol.class);
public final PeerAddress peerAddress;
// provided data
private final String id;
private final Trade trade; private final Trade trade;
private final Offer offer; private final OffererAsBuyerProtocolListener listener;
private final OffererPaymentProtocolListener offererPaymentProtocolListener; private final WorkerResultHandler resultHandler;
private final WorkerFaultHandler faultHandler;
private final MessageFacade messageFacade; private final MessageFacade messageFacade;
private final WalletFacade walletFacade; private final WalletFacade walletFacade;
private final BlockChainFacade blockChainFacade; private final BlockChainFacade blockChainFacade;
private final CryptoFacade cryptoFacade; private final CryptoFacade cryptoFacade;
private final User user; private final User user;
private PeerAddress peerAddress; // private
private boolean isTakeOfferRequested; private final SequenceScheduler scheduler_1;
private int currentStep = 0; // data written/read by tasks
private String preparedOffererDepositTxAsHex; private String preparedOffererDepositTxAsHex;
private Transaction depositTransaction; private long offererTxOutIndex;
private String offererPubKey;
// data written by messages, read by tasks
private String takeOfferFeeTxId;
private String takerMultiSigPubKey;
private String takerPayoutAddress; private String takerPayoutAddress;
private String peersAccountId;
private BankAccount peersBankAccount;
private String takerMessagePubKey;
private String peersContractAsJson;
private String signedTakerDepositTxAsHex;
private String txConnOutAsHex;
private String txScriptSigAsHex;
private long takerTxOutIndex;
private SequenceScheduler scheduler_2;
private SequenceScheduler scheduler_3;
private SequenceScheduler scheduler_4;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public OffererPaymentProtocol(Trade trade, public OffererAsBuyerProtocol(Trade trade,
OffererPaymentProtocolListener offererPaymentProtocolListener, PeerAddress peerAddress,
MessageFacade messageFacade, MessageFacade messageFacade,
WalletFacade walletFacade, WalletFacade walletFacade,
BlockChainFacade blockChainFacade, BlockChainFacade blockChainFacade,
CryptoFacade cryptoFacade, CryptoFacade cryptoFacade,
User user) User user,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
OffererAsBuyerProtocolListener listener)
{ {
checkNotNull(trade);
checkNotNull(messageFacade);
checkNotNull(walletFacade);
checkNotNull(blockChainFacade);
checkNotNull(cryptoFacade);
checkNotNull(user);
this.trade = trade; this.trade = trade;
this.offererPaymentProtocolListener = offererPaymentProtocolListener; this.peerAddress = peerAddress;
this.listener = listener;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
this.messageFacade = messageFacade; this.messageFacade = messageFacade;
this.walletFacade = walletFacade; this.walletFacade = walletFacade;
this.blockChainFacade = blockChainFacade; this.blockChainFacade = blockChainFacade;
this.cryptoFacade = cryptoFacade; this.cryptoFacade = cryptoFacade;
this.user = user; this.user = user;
offer = trade.getOffer(); id = trade.getId();
messageFacade.addOffererPaymentProtocol(this); messageFacade.addOffererPaymentProtocol(this);
log.debug("0 Constr"); log.debug("OffererAsBuyerProtocol created");
List<Worker> tasks = new ArrayList<>();
tasks.add(new HandleTakeOfferRequest(resultHandler, faultHandler));
scheduler_1 = new SequenceScheduler(tasks, this);
scheduler_1.execute();
}
public void onTakeOfferFeePayedMessage(TakeOfferFeePayedMessage message)
{
log.debug("onTakeOfferFeePayedMessage");
getTrade().setTakeOfferFeeTxID(message.getTakeOfferFeeTxID());
getTrade().setTradeAmount(message.getTradeAmount());
takeOfferFeeTxId = message.getTakeOfferFeeTxID();
takerMultiSigPubKey = message.getTakerMultiSigPubKey();
if (scheduler_1.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyTakeOfferFeePayment(getResultHandler(), getFaultHandler()));
tasks.add(new CreateDepositTx(getResultHandler(), getFaultHandler()));
tasks.add(new RequestTakerDepositPayment(getResultHandler(), getFaultHandler()));
scheduler_2 = new SequenceScheduler(tasks, this);
scheduler_2.execute();
}
else
{
log.error("scheduler_1 has not completed yet.");
}
}
public void onRequestOffererPublishDepositTxMessage(RequestOffererPublishDepositTxMessage message)
{
log.debug("onRequestOffererPublishDepositTxMessage");
takerPayoutAddress = message.getTakerPayoutAddress();
peersAccountId = message.getAccountId();
peersBankAccount = message.getBankAccount();
takerMessagePubKey = message.getTakerMessagePubKey();
peersContractAsJson = message.getContractAsJson();
signedTakerDepositTxAsHex = message.getSignedTakerDepositTxAsHex();
txConnOutAsHex = message.getTxConnOutAsHex();
txScriptSigAsHex = message.getTxScriptSigAsHex();
takerTxOutIndex = message.getTakerTxOutIndex();
if (scheduler_2.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyTakerAccount(getResultHandler(), getFaultHandler()));
tasks.add(new VerifyAndSignContract(getResultHandler(), getFaultHandler()));
tasks.add(new SignAndPublishDepositTx(getResultHandler(), getFaultHandler()));
tasks.add(new SendDepositTxIdToTaker(getResultHandler(), getFaultHandler()));
tasks.add(new SetupListenerForBlockChainConfirmation(getResultHandler(), getFaultHandler()));
scheduler_3 = new SequenceScheduler(tasks, this);
scheduler_3.execute();
}
else
{
log.error("scheduler_2 has not completed yet.");
}
}
// Triggered from UI event: Button click "Bank transfer inited"
public void onUIEventBankTransferInited()
{
log.debug("onUIEventBankTransferInited");
if (scheduler_3.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new SendSignedPayoutTx(getResultHandler(), getFaultHandler()));
scheduler_4 = new SequenceScheduler(tasks, this);
scheduler_4.execute();
}
else
{
log.error("scheduler_3 has not completed yet.");
}
}
public void onPayoutTxPublishedMessage(PayoutTxPublishedMessage tradeMessage)
{
log.debug("onPayoutTxPublishedMessage");
listener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return id;
}
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxId;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public String getPeersAccountId()
{
return peersAccountId;
}
public BankAccount getPeersBankAccount()
{
return peersBankAccount;
}
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
public String getPeersContractAsJson()
{
return peersContractAsJson;
}
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
public String getTxScriptSigAsHex()
{
return txScriptSigAsHex;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public void setPreparedOffererDepositTxAsHex(String preparedOffererDepositTxAsHex)
{
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public void setOffererTxOutIndex(long offererTxOutIndex)
{
this.offererTxOutIndex = offererTxOutIndex;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public void setOffererPubKey(String offererPubKey)
{
this.offererPubKey = offererPubKey;
}
public Trade getTrade()
{
return trade;
}
public OffererAsBuyerProtocolListener getListener()
{
return listener;
}
public WorkerResultHandler getResultHandler()
{
return resultHandler;
}
public WorkerFaultHandler getFaultHandler()
{
return faultHandler;
}
public MessageFacade getMessageFacade()
{
return messageFacade;
}
public WalletFacade getWalletFacade()
{
return walletFacade;
}
public BlockChainFacade getBlockChainFacade()
{
return blockChainFacade;
}
public CryptoFacade getCryptoFacade()
{
return cryptoFacade;
}
public User getUser()
{
return user;
} }
@ -98,7 +330,7 @@ public class OffererPaymentProtocol
// We got a take offer request and check if the offer is not already reserved for another user. // 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. // If the offer is free we send an accept message.
public void onTakeOfferRequested(PeerAddress sender) /* public void onTakeOfferRequested(PeerAddress sender)
{ {
log.debug("1.3 onTakeOfferRequested"); log.debug("1.3 onTakeOfferRequested");
@ -108,7 +340,7 @@ public class OffererPaymentProtocol
if (isTakeOfferRequested) if (isTakeOfferRequested)
{ {
log.debug("1.3 offer already requested REJECT_TAKE_OFFER_REQUEST"); log.debug("1.3 offer already requested REJECT_TAKE_OFFER_REQUEST");
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REJECT_TAKE_OFFER_REQUEST, trade.getId()); TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REJECT_TAKE_OFFER_REQUEST, trade.getId());
TradeMessageListener listener = new TradeMessageListener() TradeMessageListener listener = new TradeMessageListener()
{ {
@Override @Override
@ -140,7 +372,7 @@ public class OffererPaymentProtocol
log.debug("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onResult"); log.debug("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onResult");
// The accept message has arrived at the peer // The accept message has arrived at the peer
// We set requested flag and remove the offer from the orderbook // We set requested flag and remove the offer from the orderbook
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
isTakeOfferRequested = true; isTakeOfferRequested = true;
log.debug("1.3 messageFacade.removeOffer"); log.debug("1.3 messageFacade.removeOffer");
@ -153,18 +385,18 @@ public class OffererPaymentProtocol
public void onFailed() public void onFailed()
{ {
log.warn("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onFailed"); log.warn("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onFailed");
offererPaymentProtocolListener.onFailure("onTakeOfferRequested onSendTradingMessageFailed"); // offererPaymentProtocolListener.onFailure("onTakeOfferRequested onSendTradingMessageFailed");
} }
}; };
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
// 1.3a Send accept take offer message // 1.3a Send accept take offer message
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.ACCEPT_TAKE_OFFER_REQUEST, trade.getId()); TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.ACCEPT_TAKE_OFFER_REQUEST, trade.getId());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} }
} }
*/
//************************************************************************************************ //************************************************************************************************
// 1.4, 2.1 - 2.2 Takers task, we wait until the next incoming message // 1.4, 2.1 - 2.2 Takers task, we wait until the next incoming message
@ -175,37 +407,36 @@ public class OffererPaymentProtocol
// Step 2.3 // Step 2.3
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onTakeOfferFeePayed(TradeMessage requestTradeMessage) /* public void onTakeOfferFeePayedMessage(SendProofOfTakerOfferFeePaymentMessage requestTradeMessage)
{ {
log.debug("2.3 onTakeOfferFeePayed"); log.debug("2.3 onTakeOfferFeePayedMessage");
trade.setTakeOfferFeeTxID(requestTradeMessage.getTakeOfferFeeTxID()); trade.setTakeOfferFeeTxID(requestTradeMessage.getTakeOfferFeeTxId());
trade.setTradeAmount(requestTradeMessage.getTradeAmount()); trade.setTradeAmount(requestTradeMessage.getTradeAmount());
verifyTakeOfferFeePayment(requestTradeMessage); verifyTakeOfferFeePayment(requestTradeMessage);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.4 // Step 2.4
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void verifyTakeOfferFeePayment(TradeMessage requestTradeMessage) /* private void verifyTakeOfferFeePayment(TradeMessageOld requestTradeMessage)
{ {
log.debug("2.4 verifyTakeOfferFeePayment"); log.debug("2.4 verifyTakeOfferFeePayment");
//TODO just dummy now, will be async //TODO just dummy now, will be async
int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(requestTradeMessage.getTakeOfferFeeTxID()); int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(requestTradeMessage.getTakeOfferFeeTxId());
if (numOfPeersSeenTx > 2) if (numOfPeersSeenTx > 2)
{ {
createDepositTx(requestTradeMessage); createDepositTx(requestTradeMessage);
} }
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.5 // Step 2.5
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions") /* private void createDepositTx(TradeMessageOld requestTradeMessage)
private void createDepositTx(TradeMessage requestTradeMessage)
{ {
checkNotNull(requestTradeMessage); checkNotNull(requestTradeMessage);
@ -222,7 +453,7 @@ public class OffererPaymentProtocol
checkNotNull(arbitratorPubKey); checkNotNull(arbitratorPubKey);
log.debug("2.5 offererCreatesMSTxAndAddPayment"); log.debug("2.5 offererCreatesMSTxAndAddPayment");
log.debug("offererInputAmount " + BtcFormatter.satoshiToString(offererInputAmount)); log.debug("offererInputAmount " + BtcFormatter.formatSatoshis(offererInputAmount));
log.debug("offerer pubkey " + offererPubKey); log.debug("offerer pubkey " + offererPubKey);
log.debug("taker pubkey " + takerPubKey); log.debug("taker pubkey " + takerPubKey);
log.debug("arbitrator pubkey " + arbitratorPubKey); log.debug("arbitrator pubkey " + arbitratorPubKey);
@ -242,13 +473,13 @@ public class OffererPaymentProtocol
} }
} }
*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.6 // Step 2.6
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void sendDepositTxAndDataForContract(String preparedOffererDepositTxAsHex, String offererPubKey, long offererTxOutIndex) /*private void sendDepositTxAndDataForContract(String preparedOffererDepositTxAsHex, String offererPubKey, long offererTxOutIndex)
{ {
log.debug("2.6 sendDepositTxAndDataForContract"); log.debug("2.6 sendDepositTxAndDataForContract");
// Send all the requested data // Send all the requested data
@ -260,7 +491,7 @@ public class OffererPaymentProtocol
{ {
log.debug("2.6 sendDepositTxAndDataForContract onResult"); log.debug("2.6 sendDepositTxAndDataForContract onResult");
// Message arrived at taker // Message arrived at taker
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
// We wait until we get the signed tx back // We wait until we get the signed tx back
} }
@ -269,19 +500,19 @@ public class OffererPaymentProtocol
public void onFailed() public void onFailed()
{ {
log.debug("2.6 sendDepositTxAndDataForContract onFailed"); log.debug("2.6 sendDepositTxAndDataForContract onFailed");
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed"); // offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
} }
}; };
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID()); BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
String accountID = user.getAccountID(); String accountID = user.getAccountId();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getId(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex); TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getId(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex);
log.debug("2.6 sendTradingMessage"); log.debug("2.6 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} } */
//************************************************************************************************ //************************************************************************************************
// 2.7 - 2.11 Takers task, we wait until the next incoming message // 2.7 - 2.11 Takers task, we wait until the next incoming message
@ -292,28 +523,28 @@ public class OffererPaymentProtocol
// Step 3.1 Incoming msg from taker // Step 3.1 Incoming msg from taker
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onDepositTxReadyForPublication(TradeMessage requestTradeMessage) /* public void onRequestOffererPublishDepositTxMessage(TradeMessageOld requestTradeMessage)
{ {
log.debug("3.1 onDepositTxReadyForPublication"); log.debug("3.1 onRequestOffererPublishDepositTxMessage");
takerPayoutAddress = requestTradeMessage.getTakerPayoutAddress(); takerPayoutAddress = requestTradeMessage.getTakerPayoutAddress();
verifyTaker(requestTradeMessage); verifyTaker(requestTradeMessage);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.2 Verify offerers account registration and against the blacklist // Step 3.2 Verify offerers account registration and against the blacklist
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void verifyTaker(TradeMessage requestTradeMessage) /* private void verifyTaker(TradeMessageOld requestTradeMessage)
{ {
log.debug("3.2 verifyTaker"); log.debug("3.2 verifyTaker");
log.debug("3.2.1 verifyAccountRegistration"); log.debug("3.2.1 verifyAccountRegistration");
if (blockChainFacade.verifyAccountRegistration()) if (blockChainFacade.verifyAccountRegistration())
{ {
log.debug("3.2.2 isAccountBlackListed"); log.debug("3.2.2 isAccountBlackListed");
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount())) if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountId(), requestTradeMessage.getBankAccount()))
{ {
offererPaymentProtocolListener.onFailure("Taker is blacklisted."); // offererPaymentProtocolListener.onFailure("Taker is blacklisted.");
} }
else else
{ {
@ -322,20 +553,19 @@ public class OffererPaymentProtocol
} }
else else
{ {
offererPaymentProtocolListener.onFailure("Takers account registration is invalid."); // offererPaymentProtocolListener.onFailure("Takers account registration is invalid.");
}
} }
} */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.3 Verify and sign the contract // Step 3.3 Verify and sign the contract
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions") /* private void verifyAndSignContract(TradeMessageOld requestTradeMessage)
private void verifyAndSignContract(TradeMessage requestTradeMessage)
{ {
Contract contract = new Contract(offer, Contract contract = new Contract(offer,
trade.getTradeAmount(), trade.getTradeAmount(),
trade.getTakeOfferFeeTxID(), trade.getTakeOfferFeeTxId(),
user.getAccountID(), user.getAccountID(),
requestTradeMessage.getAccountID(), requestTradeMessage.getAccountID(),
user.getCurrentBankAccount(), user.getCurrentBankAccount(),
@ -364,13 +594,13 @@ public class OffererPaymentProtocol
{ {
log.error("3.3 verifyContract failed"); log.error("3.3 verifyContract failed");
} }
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.4 Sign and publish the deposit tx // Step 3.4 Sign and publish the deposit tx
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void signAndPublishDepositTx(TradeMessage requestTradeMessage) /* private void signAndPublishDepositTx(TradeMessageOld requestTradeMessage)
{ {
log.debug("3.4 signAndPublishDepositTx"); log.debug("3.4 signAndPublishDepositTx");
@ -410,13 +640,13 @@ public class OffererPaymentProtocol
e.getStackTrace();// Could not understand form of connected output script: RETURN e.getStackTrace();// Could not understand form of connected output script: RETURN
} }
} }
*/
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.5 Send tx id of published deposit tx to taker // Step 3.5 Send tx id of published deposit tx to taker
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void sendDepositTxIdToTaker(Transaction transaction) /*private void sendDepositTxIdToTaker(Transaction transaction)
{ {
log.debug("3.5 sendDepositTxIdToTaker"); log.debug("3.5 sendDepositTxIdToTaker");
@ -426,24 +656,24 @@ public class OffererPaymentProtocol
public void onResult() public void onResult()
{ {
log.debug("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onResult"); log.debug("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onResult");
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
} }
@Override @Override
public void onFailed() public void onFailed()
{ {
log.warn("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onFailed"); log.warn("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onFailed");
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed"); // offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
} }
}; };
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getId(), Utils.bytesToHexString(transaction.bitcoinSerialize())); TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getId(), Utils.bytesToHexString(transaction.bitcoinSerialize()));
log.debug("3.5 sendTradingMessage"); log.debug("3.5 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
// wait for at least 1 confirmation, then pay Fiat // wait for at least 1 confirmation, then pay Fiat
offererPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID()); offererPaymentProtocolListener.onDepositTxPublishedMessage(tradeMessage.getDepositTxID());
setupListenerForBlockChainConfirmation(transaction); setupListenerForBlockChainConfirmation(transaction);
} } */
//************************************************************************************************ //************************************************************************************************
// 3.6 Taker got informed, but no action from his side required. // 3.6 Taker got informed, but no action from his side required.
@ -451,10 +681,10 @@ public class OffererPaymentProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.7 We setup a listener for block chain confirmation // Step 3.7 We init a listener for block chain confirmation
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void setupListenerForBlockChainConfirmation(Transaction transaction) /* private void setupListenerForBlockChainConfirmation(Transaction transaction)
{ {
log.debug("3.7 setupListenerForBlockChainConfirmation"); log.debug("3.7 setupListenerForBlockChainConfirmation");
@ -477,6 +707,7 @@ public class OffererPaymentProtocol
onDepositTxConfirmedInBlockchain(); onDepositTxConfirmedInBlockchain();
transaction.getConfidenceForAddress().removeEventListener(this); transaction.getConfidenceForAddress().removeEventListener(this);
} */ } */
/*
} }
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
@ -487,27 +718,27 @@ public class OffererPaymentProtocol
} }
} }
); );
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.8 We check if the block chain confirmation is >= 1 // Step 3.8 We check if the block chain confirmation is >= 1
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void updateConfirmation(TransactionConfidence confidence) /* private void updateConfirmation(TransactionConfidence confidence)
{ {
log.debug("3.8 updateConfirmation " + confidence); log.debug("3.8 updateConfirmation " + confidence);
offererPaymentProtocolListener.onDepositTxConfirmedUpdate(confidence); offererPaymentProtocolListener.onDepositTxConfirmedUpdate(confidence);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.9 Blockchain confirmation received, so tell user he should start bank transfer // Step 3.9 Blockchain confirmation received, so tell user he should start bank transfer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void onDepositTxConfirmedInBlockchain() /* private void onDepositTxConfirmedInBlockchain()
{ {
log.debug("3.9 readyForBankTransfer"); log.debug("3.9 readyForBankTransfer");
offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain(); offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
} } */
//************************************************************************************************ //************************************************************************************************
// Offerer need to start bank tx, after he done it he call the next step // Offerer need to start bank tx, after he done it he call the next step
@ -518,25 +749,25 @@ public class OffererPaymentProtocol
// Step 3.10 User clicked the "bank transfer inited" button, so we tell the peer that we started the bank tx // Step 3.10 User clicked the "bank transfer inited" button, so we tell the peer that we started the bank tx
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions") /*
public void bankTransferInited() public void onBankTransferInitedMessage()
{ {
log.debug("3.10 bankTransferInited"); log.debug("3.10 onBankTransferInitedMessage");
TradeMessageListener listener = new TradeMessageListener() TradeMessageListener listener = new TradeMessageListener()
{ {
@Override @Override
public void onResult() public void onResult()
{ {
log.debug("3.10 bankTransferInited BANK_TX_INITED onResult"); log.debug("3.10 onBankTransferInitedMessage BANK_TX_INITED onResult");
offererPaymentProtocolListener.onProgress(getProgress()); // offererPaymentProtocolListener.onProgress(getProgress());
} }
@Override @Override
public void onFailed() public void onFailed()
{ {
log.warn("3.10 bankTransferInited BANK_TX_INITED onFailed"); log.warn("3.10 onBankTransferInitedMessage BANK_TX_INITED onFailed");
offererPaymentProtocolListener.onFailure("bankTransferInited BANK_TX_INITED"); // offererPaymentProtocolListener.onFailure("onBankTransferInitedMessage BANK_TX_INITED");
} }
}; };
@ -560,7 +791,7 @@ public class OffererPaymentProtocol
String offererSignatureS = offererSignature.s.toString(); String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue(); String depositTxAsHex = result.getValue();
String offererPayoutAddress = walletFacade.getAddressInfoByTradeID(trade.getId()).getAddressString(); String offererPayoutAddress = walletFacade.getAddressInfoByTradeID(trade.getId()).getAddressString();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getId(), TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.BANK_TX_INITED, trade.getId(),
depositTxAsHex, depositTxAsHex,
offererSignatureR, offererSignatureR,
offererSignatureS, offererSignatureS,
@ -583,7 +814,7 @@ public class OffererPaymentProtocol
log.error("3.10 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage()); log.error("3.10 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
} }
} }
*/
//************************************************************************************************ //************************************************************************************************
// We wait until taker has received the money on his bank account, that might take a while. // We wait until taker has received the money on his bank account, that might take a while.
@ -594,25 +825,23 @@ public class OffererPaymentProtocol
// Step 3.14 We received the payout tx. Trade is completed // Step 3.14 We received the payout tx. Trade is completed
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onPayoutTxPublished(TradeMessage tradeMessage) /* public void onPayoutTxPublishedMessage(PayoutTxPublishedMessage tradeMessage)
{ {
log.debug("3.14 onPayoutTxPublished"); log.debug("onPayoutTxPublishedMessage");
log.debug("3.14 TRADE COMPLETE!!!!!!!!!!!"); offererPaymentProtocolListener.onPayoutTxPublishedMessage(tradeMessage.getPayoutTxAsHex());
} */
offererPaymentProtocolListener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Util // Util
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private double getProgress() /* private double getProgress()
{ {
currentStep++; currentStep++;
int numberOfSteps = 10; int numberOfSteps = 10;
return (double) currentStep / (double) numberOfSteps; return (double) currentStep / (double) numberOfSteps;
} } */
} }

View file

@ -2,19 +2,12 @@ package io.bitsquare.trade.payment.offerer;
import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.TransactionConfidence;
@SuppressWarnings("EmptyMethod") public interface OffererAsBuyerProtocolListener
public interface OffererPaymentProtocolListener
{ {
@SuppressWarnings("UnusedParameters")
void onProgress(double progress);
void onFailure(String failureMessage);
void onDepositTxPublished(String depositTxID); void onDepositTxPublished(String depositTxID);
void onDepositTxConfirmedInBlockchain(); void onDepositTxConfirmedInBlockchain();
@SuppressWarnings("UnusedParameters")
void onDepositTxConfirmedUpdate(TransactionConfidence confidence); void onDepositTxConfirmedUpdate(TransactionConfidence confidence);
void onPayoutTxPublished(String payoutTxID); void onPayoutTxPublished(String payoutTxID);

View file

@ -0,0 +1,22 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class AcceptTakeOfferRequestMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 6177387534087739018L;
private final String tradeId;
public AcceptTakeOfferRequestMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,70 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
import java.math.BigInteger;
public class BankTransferInitedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -3479634129543632523L;
private final String tradeId;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
public BankTransferInitedMessage(String tradeId, String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererPayoutAddress)
{
this.tradeId = tradeId;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
this.takerPaybackAmount = takerPaybackAmount;
this.offererPayoutAddress = offererPayoutAddress;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
}

View file

@ -0,0 +1,30 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class DepositTxPublishedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -1532231540167406581L;
private final String tradeId;
private String depositTxAsHex;
public DepositTxPublishedMessage(String tradeId, String depositTxAsHex)
{
this.tradeId = tradeId;
this.depositTxAsHex = depositTxAsHex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
}

View file

@ -0,0 +1,21 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RejectTakeOfferRequestMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -8088557759642128139L;
private final String tradeId;
public RejectTakeOfferRequestMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,59 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestTakerDepositPaymentMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -3988720410493712913L;
private final String tradeId;
private BankAccount bankAccount;
private String accountID;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
public RequestTakerDepositPaymentMessage(String tradeId, BankAccount bankAccount, String accountID, String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex)
{
this.tradeId = tradeId;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.offererPubKey = offererPubKey;
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public BankAccount getBankAccount()
{
return bankAccount;
}
public String getAccountID()
{
return accountID;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.nucleo.scheduler.tasks.AbstractTask;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractOffererAsBuyerTask extends AbstractTask
{
private static final Logger log = LoggerFactory.getLogger(AbstractOffererAsBuyerTask.class);
protected OffererAsBuyerProtocol sharedModel;
public AbstractOffererAsBuyerTask(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
addResultHandlers(resultHandler);
addFaultHandlers(faultHandler);
}
@Override
public void setModel(Object model)
{
sharedModel = (OffererAsBuyerProtocol) model;
super.setModel(model);
}
}

View file

@ -0,0 +1,44 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateDepositTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(CreateDepositTx.class);
public CreateDepositTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.setOffererPubKey(sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getPubKeyAsHexString());
Transaction tx = sharedModel.getWalletFacade().offererCreatesMSTxAndAddPayment(sharedModel.getTrade().getCollateralAmount(),
sharedModel.getOffererPubKey(),
sharedModel.getTakerMultiSigPubKey(),
sharedModel.getTrade().getOffer().getArbitrator().getPubKeyAsHex(),
sharedModel.getTrade().getId());
sharedModel.setPreparedOffererDepositTxAsHex(Utils.bytesToHexString(tx.bitcoinSerialize()));
sharedModel.setOffererTxOutIndex(tx.getInput(0).getOutpoint().getIndex());
complete();
} catch (InsufficientMoneyException e)
{
log.error("Create deposit tx failed due InsufficientMoneyException " + e);
failed(new Exception("Create deposit tx failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,70 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.payment.offerer.messages.AcceptTakeOfferRequestMessage;
import io.bitsquare.trade.payment.offerer.messages.RejectTakeOfferRequestMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HandleTakeOfferRequest extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(HandleTakeOfferRequest.class);
public HandleTakeOfferRequest(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
if (sharedModel.getTrade().getState() == Trade.State.OPEN)
{
AcceptTakeOfferRequestMessage msg = new AcceptTakeOfferRequestMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("AcceptTakeOfferRequestMessage successfully arrived at peer");
sharedModel.getTrade().setState(Trade.State.ACCEPTED);
sharedModel.getMessageFacade().removeOffer(sharedModel.getTrade().getOffer());
complete();
}
@Override
public void onFailed()
{
log.error("AcceptTakeOfferRequestMessage failed to arrive at peer");
failed(new Exception("AcceptTakeOfferRequestMessage failed to arrive at peer"));
}
});
}
else
{
RejectTakeOfferRequestMessage msg = new RejectTakeOfferRequestMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RejectTakeOfferRequestMessage successfully arrived at peer");
}
@Override
public void onFailed()
{
log.error("RejectTakeOfferRequestMessage failed to arrive at peer");
}
});
log.error("Offer not marked as open.");
failed(new Exception("Offer not marked as open."));
}
}
}

View file

@ -0,0 +1,48 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.RequestTakerDepositPaymentMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakerDepositPayment extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(RequestTakerDepositPayment.class);
public RequestTakerDepositPayment(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestTakerDepositPaymentMessage tradeMessage = new RequestTakerDepositPaymentMessage(sharedModel.getTrade().getId(),
sharedModel.getUser().getBankAccount(sharedModel.getTrade().getOffer().getBankAccountUID()),
sharedModel.getUser().getAccountID(),
sharedModel.getOffererPubKey(),
sharedModel.getPreparedOffererDepositTxAsHex(),
sharedModel.getOffererTxOutIndex());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestTakerDepositPaymentMessage failed to arrive at peer");
failed(new Exception("RequestTakerDepositPaymentMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Utils;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.DepositTxPublishedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendDepositTxIdToTaker extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SendDepositTxIdToTaker.class);
public SendDepositTxIdToTaker(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(sharedModel.getTrade().getId(), Utils.bytesToHexString(sharedModel.getTrade().getDepositTransaction().bitcoinSerialize()));
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("DepositTxPublishedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("DepositTxPublishedMessage failed to arrive at peer");
failed(new Exception("DepositTxPublishedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,91 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.BankTransferInitedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedPayoutTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SendSignedPayoutTx.class);
public SendSignedPayoutTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
Transaction depositTransaction = sharedModel.getTrade().getDepositTransaction();
BigInteger collateral = sharedModel.getTrade().getCollateralAmount();
BigInteger offererPaybackAmount = sharedModel.getTrade().getTradeAmount().add(collateral);
BigInteger takerPaybackAmount = collateral;
log.trace("offererPaybackAmount " + offererPaybackAmount);
log.trace("takerPaybackAmount " + takerPaybackAmount);
log.trace("depositTransaction.getHashAsString() " + depositTransaction.getHashAsString());
log.trace("takerPayoutAddress " + sharedModel.getTakerPayoutAddress());
Pair<ECKey.ECDSASignature, String> result = sharedModel.getWalletFacade().offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(),
offererPaybackAmount,
takerPaybackAmount,
sharedModel.getTakerPayoutAddress(),
sharedModel.getTrade().getId());
ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue();
String offererPayoutAddress = sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getAddressString();
BankTransferInitedMessage tradeMessage = new BankTransferInitedMessage(sharedModel.getTrade().getId(),
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
log.trace("depositTxAsHex " + depositTxAsHex);
log.trace("offererSignatureR " + offererSignatureR);
log.trace("offererSignatureS " + offererSignatureS);
log.trace("offererPaybackAmount " + offererPaybackAmount);
log.trace("takerPaybackAmount " + takerPaybackAmount);
log.trace("offererPayoutAddress " + offererPayoutAddress);
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("BankTransferInitedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("BankTransferInitedMessage failed to arrive at peer");
failed(new Exception("BankTransferInitedMessage failed to arrive at peer"));
}
});
} catch (Exception e)
{
log.error("Exception at OffererCreatesAndSignsPayoutTx " + e);
failed(e);
}
}
}

View file

@ -0,0 +1,48 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SetupListenerForBlockChainConfirmation extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class);
public SetupListenerForBlockChainConfirmation(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO
// sharedModel.offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
Transaction tx = sharedModel.getTrade().getDepositTransaction();
tx.getConfidence().addEventListener(new TransactionConfidence.Listener()
{
@Override
public void onConfidenceChanged(Transaction tx, ChangeReason reason)
{
log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == ChangeReason.SEEN_PEERS)
{
sharedModel.getListener().onDepositTxConfirmedUpdate(tx.getConfidence());
}
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
{
sharedModel.getListener().onDepositTxConfirmedInBlockchain();
tx.getConfidence().removeEventListener(this);
log.trace("Tx is in blockchain");
complete();
}
}
}
);
}
}

View file

@ -0,0 +1,56 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishDepositTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTx.class);
public SignAndPublishDepositTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.getWalletFacade().offererSignAndPublishTx(sharedModel.getPreparedOffererDepositTxAsHex(),
sharedModel.getSignedTakerDepositTxAsHex(),
sharedModel.getTxConnOutAsHex(),
sharedModel.getTxScriptSigAsHex(),
sharedModel.getOffererTxOutIndex(),
sharedModel.getTakerTxOutIndex(),
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.trace("offererSignAndPublishTx succeeded " + transaction);
sharedModel.getTrade().setDepositTransaction(transaction);
sharedModel.getListener().onDepositTxPublished(transaction.getHashAsString());
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("offererSignAndPublishTx failed:" + t);
failed(t);
}
});
} catch (Exception e)
{
log.error("offererSignAndPublishTx failed:" + e);
failed(e);
}
}
}

View file

@ -0,0 +1,59 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.trade.Contract;
import io.bitsquare.util.Utilities;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyAndSignContract extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyAndSignContract.class);
public VerifyAndSignContract(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
Contract contract = new Contract(sharedModel.getTrade().getOffer(),
sharedModel.getTrade().getTradeAmount(),
sharedModel.getTrade().getTakeOfferFeeTxId(),
sharedModel.getUser().getAccountID(),
sharedModel.getPeersAccountId(),
sharedModel.getUser().getCurrentBankAccount(),
sharedModel.getPeersBankAccount(),
sharedModel.getTrade().getOffer().getMessagePubKeyAsHex(),
sharedModel.getTakerMessagePubKey());
String contractAsJson = Utilities.objectToJson(contract);
// log.trace("Offerer contract created: " + contract);
// log.trace("Offerers contractAsJson: " + contractAsJson);
// log.trace("Takers contractAsJson: " + sharedModel.peersContractAsJson);
if (contractAsJson.equals(sharedModel.getPeersContractAsJson()))
{
log.trace("The 2 contracts as json does match");
String signature = sharedModel.getCryptoFacade().signContract(sharedModel.getWalletFacade().getRegistrationAddressInfo().getKey(), contractAsJson);
sharedModel.getTrade().setContract(contract);
sharedModel.getTrade().setContractAsJson(contractAsJson);
sharedModel.getTrade().setContractTakerSignature(signature);
//log.trace("signature: " + signature);
complete();
}
else
{
// TODO use diff output as feedback ?
log.error("Contracts are not matching.");
log.error("Offerers contractAsJson: " + contractAsJson);
log.error("Takers contractAsJson: " + sharedModel.getPeersContractAsJson());
failed(new Exception("Contracts are not matching"));
}
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakeOfferFeePayment extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakeOfferFeePayment.class);
public VerifyTakeOfferFeePayment(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet, need a confidence listeners
int numOfPeersSeenTx = sharedModel.getWalletFacade().getNumOfPeersSeenTx(sharedModel.getTakeOfferFeeTxId());
if (numOfPeersSeenTx > 2)
{
complete();
}
}
}

View file

@ -0,0 +1,41 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakerAccount extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakerAccount.class);
public VerifyTakerAccount(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet
if (sharedModel.getBlockChainFacade().verifyAccountRegistration())
{
if (sharedModel.getBlockChainFacade().isAccountBlackListed(sharedModel.getPeersAccountId(), sharedModel.getPeersBankAccount()))
{
log.error("Taker is blacklisted");
failed(new Exception("Taker is blacklisted"));
}
else
{
complete();
}
}
else
{
log.error("Account registration validation for peer failed.");
failed(new Exception("Account registration validation for peer failed."));
}
}
}

View file

@ -1,58 +1,78 @@
package io.bitsquare.trade.payment.taker; package io.bitsquare.trade.payment.taker;
import com.google.bitcoin.core.AddressFormatException;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.bank.BankAccount; import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade; import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade; import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade; import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade; 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.trade.Trade;
import io.bitsquare.trade.payment.offerer.messages.BankTransferInitedMessage;
import io.bitsquare.trade.payment.offerer.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.payment.offerer.messages.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.payment.taker.tasks.*;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import io.bitsquare.util.Utilities; import io.nucleo.scheduler.SequenceScheduler;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO use states //TODO use states
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public class TakerPaymentProtocol public class TakerAsSellerProtocol
{ {
private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class); private static final Logger log = LoggerFactory.getLogger(TakerAsSellerProtocol.class);
// provided data
private final String id;
private final Trade trade; private final Trade trade;
private final Offer offer; private final TakerAsSellerProtocolListener listener;
private final TakerPaymentProtocolListener takerPaymentProtocolListener; private final WorkerResultHandler resultHandler;
private final WorkerFaultHandler faultHandler;
private final MessageFacade messageFacade; private final MessageFacade messageFacade;
private final WalletFacade walletFacade; private final WalletFacade walletFacade;
private final BlockChainFacade blockChainFacade; private final BlockChainFacade blockChainFacade;
private final CryptoFacade cryptoFacade; private final CryptoFacade cryptoFacade;
private final User user; private final User user;
// private
private final SequenceScheduler scheduler_1;
// written/read by task
private String payoutTxAsHex;
private PeerAddress peerAddress; private PeerAddress peerAddress;
private Transaction signedTakerDepositTx;
private long takerTxOutIndex;
// written by message, read by tasks
private String peersAccountId;
private BankAccount peersBankAccount;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
private int currentStep = 0; private int currentStep = 0;
private SequenceScheduler scheduler_2;
private SequenceScheduler scheduler_3;
private SequenceScheduler scheduler_4;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor // Constructor
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public TakerPaymentProtocol(Trade trade, public TakerAsSellerProtocol(Trade trade,
TakerPaymentProtocolListener takerPaymentProtocolListener, TakerAsSellerProtocolListener listener,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
MessageFacade messageFacade, MessageFacade messageFacade,
WalletFacade walletFacade, WalletFacade walletFacade,
BlockChainFacade blockChainFacade, BlockChainFacade blockChainFacade,
@ -60,32 +80,271 @@ public class TakerPaymentProtocol
User user) User user)
{ {
this.trade = trade; this.trade = trade;
this.takerPaymentProtocolListener = takerPaymentProtocolListener; this.listener = listener;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
this.messageFacade = messageFacade; this.messageFacade = messageFacade;
this.walletFacade = walletFacade; this.walletFacade = walletFacade;
this.blockChainFacade = blockChainFacade; this.blockChainFacade = blockChainFacade;
this.cryptoFacade = cryptoFacade; this.cryptoFacade = cryptoFacade;
this.user = user; this.user = user;
offer = trade.getOffer(); id = trade.getId();
log.debug("TakerAsSellerProtocol created");
messageFacade.addTakerPaymentProtocol(this); messageFacade.addTakerPaymentProtocol(this);
// start with first task sequence
List<Worker> tasks = new ArrayList<>();
tasks.add(new GetPeerAddress(resultHandler, faultHandler));
tasks.add(new RequestTakeOffer(resultHandler, faultHandler));
scheduler_1 = new SequenceScheduler(tasks, this);
scheduler_1.execute();
} }
public void takeOffer() public void onAcceptTakeOfferRequestMessage()
{ {
log.debug("1 takeOffer"); log.debug("onAcceptTakeOfferRequestMessage");
findPeerAddress(); if (scheduler_1.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new PayTakeOfferFee(resultHandler, faultHandler));
tasks.add(new SendTakeOfferFeePayedTxId(resultHandler, faultHandler));
scheduler_2 = new SequenceScheduler(tasks, this);
scheduler_2.execute();
}
else
{
log.error("scheduler_1 has not completed yet.");
}
} }
public void onRejectTakeOfferRequestMessage()
{
log.debug("onRejectTakeOfferRequestMessage");
if (scheduler_1.getHasCompleted())
{
//TODO
}
else
{
log.error("scheduler_1 has not completed yet.");
}
}
public void onRequestTakerDepositPaymentMessage(RequestTakerDepositPaymentMessage message)
{
log.debug("onRequestTakerDepositPaymentMessage");
peersAccountId = message.getAccountID();
peersBankAccount = message.getBankAccount();
offererPubKey = message.getOffererPubKey();
preparedOffererDepositTxAsHex = message.getPreparedOffererDepositTxAsHex();
offererTxOutIndex = message.getOffererTxOutIndex();
if (scheduler_2.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyOffererAccount(resultHandler, faultHandler));
tasks.add(new CreateAndSignContract(resultHandler, faultHandler));
tasks.add(new PayDeposit(resultHandler, faultHandler));
tasks.add(new SendSignedTakerDepositTxAsHex(resultHandler, faultHandler));
scheduler_3 = new SequenceScheduler(tasks, this);
scheduler_3.execute();
}
else
{
log.error("scheduler_2 has not completed yet.");
}
}
// informational, does only trigger UI feedback/update
public void onDepositTxPublishedMessage(DepositTxPublishedMessage message)
{
log.debug("onDepositTxPublishedMessage");
String txID = getWalletFacade().takerCommitDepositTx(message.getDepositTxAsHex());
listener.onDepositTxPublished(txID);
}
// informational, store data for later, does only trigger UI feedback/update
public void onBankTransferInitedMessage(BankTransferInitedMessage tradeMessage)
{
log.debug("onBankTransferInitedMessage");
depositTxAsHex = tradeMessage.getDepositTxAsHex();
offererSignatureR = tradeMessage.getOffererSignatureR();
offererSignatureS = tradeMessage.getOffererSignatureS();
offererPaybackAmount = tradeMessage.getOffererPaybackAmount();
takerPaybackAmount = tradeMessage.getTakerPaybackAmount();
offererPayoutAddress = tradeMessage.getOffererPayoutAddress();
listener.onBankTransferInited(tradeMessage.getTradeId());
}
// User clicked the "bank transfer received" button, so we release the funds for pay out
public void onUIEventFiatReceived()
{
log.debug("onUIEventFiatReceived");
if (scheduler_3.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new SignAndPublishPayoutTx(resultHandler, faultHandler));
tasks.add(new SendPayoutTxToOfferer(resultHandler, faultHandler));
scheduler_4 = new SequenceScheduler(tasks, this);
scheduler_4.execute();
}
else
{
log.error("scheduler_3 has not completed yet.");
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return id;
}
public Trade getTrade()
{
return trade;
}
public TakerAsSellerProtocolListener getListener()
{
return listener;
}
public MessageFacade getMessageFacade()
{
return messageFacade;
}
public WalletFacade getWalletFacade()
{
return walletFacade;
}
public BlockChainFacade getBlockChainFacade()
{
return blockChainFacade;
}
public CryptoFacade getCryptoFacade()
{
return cryptoFacade;
}
public User getUser()
{
return user;
}
public PeerAddress getPeerAddress()
{
return peerAddress;
}
public void setPeerAddress(PeerAddress peerAddress)
{
this.peerAddress = peerAddress;
}
public Transaction getSignedTakerDepositTx()
{
return signedTakerDepositTx;
}
public void setSignedTakerDepositTx(Transaction signedTakerDepositTx)
{
this.signedTakerDepositTx = signedTakerDepositTx;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
public void setTakerTxOutIndex(long takerTxOutIndex)
{
this.takerTxOutIndex = takerTxOutIndex;
}
public String getPeersAccountId()
{
return peersAccountId;
}
public BankAccount getPeersBankAccount()
{
return peersBankAccount;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
public void setPayoutTxAsHex(String payoutTxAsHex)
{
this.payoutTxAsHex = payoutTxAsHex;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 1.1 // Step 1.1
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void findPeerAddress()
/* private void findPeerAddress()
{ {
log.debug("1.1 findPeerAddress"); log.debug("1.1 findPeerAddress");
AddressLookupListener addressLookupListener = new AddressLookupListener() AddressLookupListener addressLookupListener = new AddressLookupListener()
@ -115,14 +374,13 @@ public class TakerPaymentProtocol
// Request the peers address from the DHT // Request the peers address from the DHT
messageFacade.getPeerAddress(offer.getMessagePubKeyAsHex(), addressLookupListener); messageFacade.getPeerAddress(offer.getMessagePubKeyAsHex(), addressLookupListener);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 1.2 // Step 1.2
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void requestTakeOffer() /* private void requestTakeOffer()
{ {
log.debug("1.2 requestTakeOffer"); log.debug("1.2 requestTakeOffer");
TradeMessageListener listener = new TradeMessageListener() TradeMessageListener listener = new TradeMessageListener()
@ -153,7 +411,7 @@ public class TakerPaymentProtocol
// Send the take offer request // Send the take offer request
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKE_OFFER, trade.getId()); TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKE_OFFER, trade.getId());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} } */
//************************************************************************************************ //************************************************************************************************
@ -167,27 +425,27 @@ public class TakerPaymentProtocol
// 1.4a offerer has accepted the take offer request. Move on to step 2. // 1.4a offerer has accepted the take offer request. Move on to step 2.
public void onTakeOfferRequestAccepted() /* public void onAcceptTakeOfferRequestMessage()
{ {
log.debug("1.4a onTakeOfferRequestAccepted"); log.debug("1.4a onAcceptTakeOfferRequestMessage");
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
payOfferFee(trade); payOfferFee(trade);
} }
// 1.4b Offerer has rejected the take offer request. The UI controller will handle the case. // 1.4b Offerer has rejected the take offer request. The UI controller will onResult the case.
public void onTakeOfferRequestRejected() public void onRejectTakeOfferRequestMessage()
{ {
log.debug("1.4b onTakeOfferRequestRejected"); log.debug("1.4b onRejectTakeOfferRequestMessage");
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.1 // Step 2.1
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void payOfferFee(Trade trade) /* private void payOfferFee(Trade trade)
{ {
log.debug("2.1 payTakeOfferFee"); log.debug("2.1 payTakeOfferFee");
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
@ -201,7 +459,7 @@ public class TakerPaymentProtocol
// Offer fee payed successfully. // Offer fee payed successfully.
trade.setTakeOfferFeeTxID(transaction.getHashAsString()); trade.setTakeOfferFeeTxID(transaction.getHashAsString());
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
// move on // move on
sendTakerOfferFeeTxID(transaction.getHashAsString()); sendTakerOfferFeeTxID(transaction.getHashAsString());
@ -211,19 +469,19 @@ public class TakerPaymentProtocol
public void onFailure(Throwable t) public void onFailure(Throwable t)
{ {
log.debug("2.1 payTakeOfferFee onFailure"); log.debug("2.1 payTakeOfferFee onFailure");
takerPaymentProtocolListener.onFailure("payTakeOfferFee onFailure " + t.getMessage()); // listener.onFailure("payTakeOfferFee onFailure " + t.getMessage());
} }
}; };
try try
{ {
// Pay the offer fee // Pay the offer fee
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
walletFacade.payTakeOfferFee(trade.getId(), callback); walletFacade.payTakeOfferFee(trade.getId(), callback);
} catch (InsufficientMoneyException e) } catch (InsufficientMoneyException e)
{ {
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
}
} }
} */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -235,7 +493,7 @@ public class TakerPaymentProtocol
// 0 block confirmation is acceptable for the fee to not block the process // 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. // The offerer will wait until a minimum of peers seen the tx before sending his data.
// We also get the multisig tx delivered // We also get the multisig tx delivered
private void sendTakerOfferFeeTxID(String takeOfferFeeTxID) /* private void sendTakerOfferFeeTxID(String takeOfferFeeTxID)
{ {
log.debug("2.2 sendTakerOfferFeeTxID"); log.debug("2.2 sendTakerOfferFeeTxID");
TradeMessageListener listener = new TradeMessageListener() TradeMessageListener listener = new TradeMessageListener()
@ -245,7 +503,7 @@ public class TakerPaymentProtocol
{ {
log.debug("2.2 sendTakerOfferFeeTxID onResult"); log.debug("2.2 sendTakerOfferFeeTxID onResult");
// Message has arrived // Message has arrived
takerPaymentProtocolListener.onProgress(getProgress()); //listener.onProgress(getProgress());
// We wait until the offerer send us the data // We wait until the offerer send us the data
} }
@ -254,21 +512,21 @@ public class TakerPaymentProtocol
public void onFailed() public void onFailed()
{ {
log.debug("2.2 sendTakerOfferFeeTxID onFailed"); log.debug("2.2 sendTakerOfferFeeTxID onFailed");
takerPaymentProtocolListener.onFailure("requestAccountDetails onSendTradingMessageFailed"); // //TakerAsSellerProtocol.this. listener.onFailure("requestAccountDetails onSendTradingMessageFailed");
} }
}; };
takerPaymentProtocolListener.onProgress(getProgress()); // this.listener.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. // 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, TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.TAKE_OFFER_FEE_PAYED,
trade.getId(), trade.getId(),
trade.getTradeAmount(), trade.getTradeAmount(),
takeOfferFeeTxID, takeOfferFeeTxID,
walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString()); walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} }
*/
//************************************************************************************************ //************************************************************************************************
// 2.3 - 2.6 Offerers tasks, we are in waiting mode // 2.3 - 2.6 Offerers tasks, we are in waiting mode
@ -279,27 +537,27 @@ public class TakerPaymentProtocol
// Step 2.7 Incoming msg from offerer // Step 2.7 Incoming msg from offerer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onTakerDepositPaymentRequested(TradeMessage requestTradeMessage) /*public void onRequestTakerDepositPaymentMessage(TradeMessageOld requestTradeMessage)
{ {
log.debug("2.7 onTakerDepositPaymentRequested"); log.debug("2.7 onRequestTakerDepositPaymentMessage");
verifyOfferer(requestTradeMessage); verifyOfferer(requestTradeMessage);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.8 Verify offerers account registration and against the blacklist // Step 2.8 Verify offerers account registration and against the blacklist
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void verifyOfferer(TradeMessage requestTradeMessage) /*private void verifyOfferer(TradeMessageOld requestTradeMessage)
{ {
log.debug("2.8 verifyOfferer"); log.debug("2.8 verifyOfferer");
log.debug("2.8.1 verifyAccountRegistration"); log.debug("2.8.1 verifyAccountRegistration");
if (blockChainFacade.verifyAccountRegistration()) if (blockChainFacade.verifyAccountRegistration())
{ {
log.debug("2.8.2 isAccountBlackListed"); log.debug("2.8.2 isAccountBlackListed");
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount())) if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountId(), requestTradeMessage.getBankAccount()))
{ {
takerPaymentProtocolListener.onFailure("Offerer is blacklisted."); // listener.onFailure("Offerer is blacklisted.");
} }
else else
{ {
@ -308,35 +566,35 @@ public class TakerPaymentProtocol
} }
else else
{ {
takerPaymentProtocolListener.onFailure("Offerers account registration is invalid."); // listener.onFailure("Offerers account registration is invalid.");
}
} }
} */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.9 Create and sign the contract // Step 2.9 Create and sign the contract
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createAndSignContract(TradeMessage requestTradeMessage) /* private void createAndSignContract(TradeMessageOld requestTradeMessage)
{ {
log.debug("2.9 createAndSignContract"); log.debug("2.9 createAndSignContract");
checkNotNull(offer); checkNotNull(trade.getOffer());
checkNotNull(trade.getTradeAmount()); checkNotNull(trade.getTradeAmount());
checkNotNull(trade.getTakeOfferFeeTxID()); checkNotNull(trade.getTakeOfferFeeTxId());
checkNotNull(requestTradeMessage.getAccountID()); checkNotNull(requestTradeMessage.getAccountId());
checkNotNull(user.getAccountID()); checkNotNull(user.getAccountId());
checkNotNull(requestTradeMessage.getBankAccount()); checkNotNull(requestTradeMessage.getBankAccount());
checkNotNull(user.getCurrentBankAccount()); checkNotNull(user.getCurrentBankAccount());
checkNotNull(user.getMessagePubKeyAsHex()); checkNotNull(user.getMessagePubKeyAsHex());
Contract contract = new Contract(offer, Contract contract = new Contract(trade.getOffer(),
trade.getTradeAmount(), trade.getTradeAmount(),
trade.getTakeOfferFeeTxID(), trade.getTakeOfferFeeTxId(),
requestTradeMessage.getAccountID(), requestTradeMessage.getAccountId(),
user.getAccountID(), user.getAccountId(),
requestTradeMessage.getBankAccount(), requestTradeMessage.getBankAccount(),
user.getCurrentBankAccount(), user.getCurrentBankAccount(),
offer.getMessagePubKeyAsHex(), trade.getOffer().getMessagePubKeyAsHex(),
user.getMessagePubKeyAsHex() user.getMessagePubKeyAsHex()
); );
@ -352,14 +610,14 @@ public class TakerPaymentProtocol
trade.setContractTakerSignature(signature); trade.setContractTakerSignature(signature);
payDeposit(requestTradeMessage); payDeposit(requestTradeMessage);
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.10 Pay in the funds to the deposit tx and sign it // Step 2.10 Pay in the funds to the deposit tx and sign it
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void payDeposit(TradeMessage requestTradeMessage) /*private void payDeposit(TradeMessageOld requestTradeMessage)
{ {
log.debug("2.10 payDeposit"); log.debug("2.10 payDeposit");
@ -369,7 +627,7 @@ public class TakerPaymentProtocol
String offererPubKey = requestTradeMessage.getOffererPubKey(); String offererPubKey = requestTradeMessage.getOffererPubKey();
String takerPubKey = walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString(); String takerPubKey = walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString();
String arbitratorPubKey = offer.getArbitrator().getPubKeyAsHex(); String arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
String preparedOffererDepositTxAsHex = requestTradeMessage.getPreparedOffererDepositTxAsHex(); String preparedOffererDepositTxAsHex = requestTradeMessage.getPreparedOffererDepositTxAsHex();
checkNotNull(takerInputAmount); checkNotNull(takerInputAmount);
@ -380,8 +638,8 @@ public class TakerPaymentProtocol
checkNotNull(preparedOffererDepositTxAsHex); checkNotNull(preparedOffererDepositTxAsHex);
log.debug("2.10 offererCreatesMSTxAndAddPayment"); log.debug("2.10 offererCreatesMSTxAndAddPayment");
log.debug("takerAmount " + BtcFormatter.satoshiToString(takerInputAmount)); log.debug("takerAmount " + BtcFormatter.formatSatoshis(takerInputAmount));
log.debug("msOutputAmount " + BtcFormatter.satoshiToString(msOutputAmount)); log.debug("msOutputAmount " + BtcFormatter.formatSatoshis(msOutputAmount));
log.debug("offerer pubkey " + offererPubKey); log.debug("offerer pubkey " + offererPubKey);
log.debug("taker pubkey " + takerPubKey); log.debug("taker pubkey " + takerPubKey);
log.debug("arbitrator pubkey " + arbitratorPubKey); log.debug("arbitrator pubkey " + arbitratorPubKey);
@ -396,14 +654,14 @@ public class TakerPaymentProtocol
{ {
log.error("2.10 error at walletFacade.takerAddPaymentAndSign: " + e.getMessage()); log.error("2.10 error at walletFacade.takerAddPaymentAndSign: " + e.getMessage());
} }
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.11 Send the tx to the offerer // Step 2.11 Send the tx to the offerer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void sendSignedTakerDepositTxAsHex(Transaction signedTakerDepositTx, long takerTxOutIndex, long offererTxOutIndex) /* private void sendSignedTakerDepositTxAsHex(Transaction signedTakerDepositTx, long takerTxOutIndex, long offererTxOutIndex)
{ {
log.debug("2.11 sendSignedTakerDepositTxAsHex"); log.debug("2.11 sendSignedTakerDepositTxAsHex");
@ -414,21 +672,21 @@ public class TakerPaymentProtocol
{ {
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onResult"); log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onResult");
// Message arrived at taker // Message arrived at taker
takerPaymentProtocolListener.onProgress(getProgress()); // //TakerAsSellerProtocol.this. listener.onFailure(getProgress());
} }
@Override @Override
public void onFailed() public void onFailed()
{ {
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed"); log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
takerPaymentProtocolListener.onFailure("sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed"); ////TakerAsSellerProtocol.this. listener.onFailure("sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
} }
}; };
takerPaymentProtocolListener.onProgress(getProgress()); // this.listener.onProgress(getProgress());
BankAccount bankAccount = user.getCurrentBankAccount(); BankAccount bankAccount = user.getCurrentBankAccount();
String accountID = user.getAccountID(); String accountID = user.getAccountId();
String messagePubKey = user.getMessagePubKeyAsHex(); String messagePubKey = user.getMessagePubKeyAsHex();
String contractAsJson = trade.getContractAsJson(); String contractAsJson = trade.getContractAsJson();
String signature = trade.getTakerSignature(); String signature = trade.getTakerSignature();
@ -443,7 +701,7 @@ public class TakerPaymentProtocol
log.debug("2.10 txConnOutAsHex: " + txConnOutAsHex); log.debug("2.10 txConnOutAsHex: " + txConnOutAsHex);
log.debug("2.10 payoutAddress: " + payoutAddress); log.debug("2.10 payoutAddress: " + payoutAddress);
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_OFFERER_DEPOSIT_PUBLICATION, TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REQUEST_OFFERER_DEPOSIT_PUBLICATION,
trade.getId(), trade.getId(),
bankAccount, bankAccount,
accountID, accountID,
@ -460,7 +718,7 @@ public class TakerPaymentProtocol
log.debug("2.11 sendTradingMessage"); log.debug("2.11 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} } */
//************************************************************************************************ //************************************************************************************************
@ -472,14 +730,14 @@ public class TakerPaymentProtocol
// Step 3.6 Incoming msg from offerer // Step 3.6 Incoming msg from offerer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onDepositTxPublished(TradeMessage tradeMessage) /* public void onDepositTxPublishedMessage(TradeMessageOld tradeMessage)
{ {
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxAsHex()); log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxAsHex());
String txID = walletFacade.takerCommitDepositTx(tradeMessage.getDepositTxAsHex()); String txID = walletFacade.takerCommitDepositTx(tradeMessage.getDepositTxAsHex());
takerPaymentProtocolListener.onProgress(getProgress()); // listener.onProgress(getProgress());
takerPaymentProtocolListener.onDepositTxPublished(txID); listener.onDepositTxPublishedMessage(txID);
} } */
//************************************************************************************************ //************************************************************************************************
@ -491,11 +749,11 @@ public class TakerPaymentProtocol
// Step 3.11 Incoming msg from offerer // Step 3.11 Incoming msg from offerer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onBankTransferInited(TradeMessage tradeMessage) /* public void onBankTransferInitedMessage(BankTransferInitedMessage tradeMessage)
{ {
log.debug("3.11 Bank transfer inited msg received"); log.debug("3.11 Bank transfer inited msg received");
takerPaymentProtocolListener.onBankTransferInited(tradeMessage); listener.onBankTransferInitedMessage(tradeMessage);
} } */
//************************************************************************************************ //************************************************************************************************
// Taker will check periodically his bank account until he received the money. That might take a while... // Taker will check periodically his bank account until he received the money. That might take a while...
@ -506,9 +764,9 @@ public class TakerPaymentProtocol
// Step 3.12 User clicked the "bank transfer received" button, so we release the funds for pay out // Step 3.12 User clicked the "bank transfer received" button, so we release the funds for pay out
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void releaseBTC(TradeMessage tradeMessage) /* public void onUIEventFiatReceived(TradeMessageOld tradeMessage)
{ {
log.debug("3.12 releaseBTC"); log.debug("3.12 onUIEventFiatReceived");
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
{ {
@Override @Override
@ -516,7 +774,7 @@ public class TakerPaymentProtocol
{ {
System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction); System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction); log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
takerPaymentProtocolListener.onTradeCompleted(transaction.getHashAsString()); listener.onTradeCompleted(transaction.getHashAsString());
sendPayoutTxToOfferer(Utils.bytesToHexString(transaction.bitcoinSerialize())); sendPayoutTxToOfferer(Utils.bytesToHexString(transaction.bitcoinSerialize()));
} }
@ -526,7 +784,7 @@ public class TakerPaymentProtocol
{ {
log.error("######### 3.12 onFailure walletFacade.takerSignsAndSendsTx"); log.error("######### 3.12 onFailure walletFacade.takerSignsAndSendsTx");
System.err.println("3.12 onFailure walletFacade.takerSignsAndSendsTx"); System.err.println("3.12 onFailure walletFacade.takerSignsAndSendsTx");
takerPaymentProtocolListener.onFailure("takerSignsAndSendsTx failed " + t.getMessage()); // listener.onFailure("takerSignsAndSendsTx failed " + t.getMessage());
} }
}; };
try try
@ -551,13 +809,13 @@ public class TakerPaymentProtocol
{ {
log.error("3.12 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage()); log.error("3.12 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
} }
} } */
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.13 Send payout txID to offerer // Step 3.13 Send payout txID to offerer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
void sendPayoutTxToOfferer(String payoutTxAsHex) /*void sendPayoutTxToOfferer(String payoutTxAsHex)
{ {
log.debug("3.13 sendPayoutTxToOfferer "); log.debug("3.13 sendPayoutTxToOfferer ");
TradeMessageListener listener = new TradeMessageListener() TradeMessageListener listener = new TradeMessageListener()
@ -567,27 +825,28 @@ public class TakerPaymentProtocol
{ {
log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onResult"); log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onResult");
log.debug("3.13 TRADE COMPLETE!!!!!!!!!!!"); log.debug("3.13 TRADE COMPLETE!!!!!!!!!!!");
takerPaymentProtocolListener.onProgress(getProgress()); //TakerAsSellerProtocol.this. listener.onFailure(getProgress());
} }
@Override @Override
public void onFailed() public void onFailed()
{ {
log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed"); log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed");
takerPaymentProtocolListener.onFailure("sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed"); //TakerAsSellerProtocol.this. listener.onFailure("sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed");
} }
}; };
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.PAYOUT_TX_PUBLISHED, trade.getId()); TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.PAYOUT_TX_PUBLISHED, trade.getId());
tradeMessage.setPayoutTxAsHex(payoutTxAsHex); tradeMessage.setPayoutTxAsHex(payoutTxAsHex);
log.debug("3.13 sendTradeMessage PAYOUT_TX_PUBLISHED"); log.debug("3.13 sendTradeMessage PAYOUT_TX_PUBLISHED");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
} } */
private double getProgress() /* private double getProgress()
{ {
currentStep++; currentStep++;
int numberOfSteps = 10; int numberOfSteps = 10;
return (double) currentStep / (double) numberOfSteps; return (double) currentStep / (double) numberOfSteps;
} } */
} }

View file

@ -0,0 +1,12 @@
package io.bitsquare.trade.payment.taker;
import io.bitsquare.trade.Trade;
public interface TakerAsSellerProtocolListener
{
void onDepositTxPublished(String depositTxId);
void onBankTransferInited(String tradeId);
void onTradeCompleted(Trade trade, String hashAsString);
}

View file

@ -1,16 +0,0 @@
package io.bitsquare.trade.payment.taker;
import io.bitsquare.msg.TradeMessage;
public interface TakerPaymentProtocolListener
{
void onProgress(double progress);
void onFailure(String failureMessage);
void onDepositTxPublished(String depositTxID);
void onBankTransferInited(TradeMessage tradeMessage);
void onTradeCompleted(String hashAsString);
}

View file

@ -1,8 +1,8 @@
package io.bitsquare.msg.listeners; package io.bitsquare.trade.payment.taker.listeners;
import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.PeerAddress;
public interface AddressLookupListener public interface GetPeerAddressListener
{ {
void onResult(PeerAddress peerAddress); void onResult(PeerAddress peerAddress);

View file

@ -0,0 +1,28 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class PayoutTxPublishedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 1288653559218403873L;
private final String tradeId;
private String payoutTxAsHex;
public PayoutTxPublishedMessage(String tradeId, String payoutTxAsHex)
{
this.tradeId = tradeId;
this.payoutTxAsHex = payoutTxAsHex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
}

View file

@ -0,0 +1,115 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 2179683654379803071L;
private final String tradeId;
private BankAccount bankAccount;
private String accountID;
private String takerMessagePubKey;
private String signedTakerDepositTxAsHex;
private String txScriptSigAsHex;
private String txConnOutAsHex;
private String contractAsJson;
private String takerContractSignature;
private String takerPayoutAddress;
private long takerTxOutIndex;
private long offererTxOutIndex;
public RequestOffererPublishDepositTxMessage(String tradeId,
BankAccount bankAccount,
String accountID,
String takerMessagePubKey,
String signedTakerDepositTxAsHex,
String txScriptSigAsHex,
String txConnOutAsHex,
String contractAsJson,
String takerContractSignature,
String takerPayoutAddress,
long takerTxOutIndex,
long offererTxOutIndex)
{
this.tradeId = tradeId;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.takerMessagePubKey = takerMessagePubKey;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.txConnOutAsHex = txConnOutAsHex;
this.contractAsJson = contractAsJson;
this.takerContractSignature = takerContractSignature;
this.takerPayoutAddress = takerPayoutAddress;
this.takerTxOutIndex = takerTxOutIndex;
this.offererTxOutIndex = offererTxOutIndex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public BankAccount getBankAccount()
{
return bankAccount;
}
public String getAccountId()
{
return accountID;
}
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
public String getTxScriptSigAsHex()
{
return txScriptSigAsHex;
}
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
public String getContractAsJson()
{
return contractAsJson;
}
public String getTakerContractSignature()
{
return takerContractSignature;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
}

View file

@ -0,0 +1,23 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestTakeOfferMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 4660151440192191798L;
private final String tradeId;
public RequestTakeOfferMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,45 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
import java.math.BigInteger;
public class TakeOfferFeePayedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -5057935061275354312L;
private final String tradeId;
private BigInteger tradeAmount;
private String takeOfferFeeTxID;
private String takerMultiSigPubKey;
public TakeOfferFeePayedMessage(String tradeId, String takeOfferFeeTxID, BigInteger tradeAmount, String takerMultiSigPubKey)
{
this.tradeId = tradeId;
this.takeOfferFeeTxID = takeOfferFeeTxID;
this.tradeAmount = tradeAmount;
this.takerMultiSigPubKey = takerMultiSigPubKey;
}
@Override
public String getTradeId()
{
return tradeId;
}
public BigInteger getTradeAmount()
{
return tradeAmount;
}
public String getTakeOfferFeeTxID()
{
return takeOfferFeeTxID;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.nucleo.scheduler.tasks.AbstractTask;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractTakerAsSellerTask extends AbstractTask
{
private static final Logger log = LoggerFactory.getLogger(AbstractTakerAsSellerTask.class);
protected TakerAsSellerProtocol sharedModel;
public AbstractTakerAsSellerTask(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
addResultHandlers(resultHandler);
addFaultHandlers(faultHandler);
}
@Override
public void setModel(Object model)
{
sharedModel = (TakerAsSellerProtocol) model;
super.setModel(model);
}
}

View file

@ -0,0 +1,49 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.util.Utilities;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateAndSignContract extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
public CreateAndSignContract(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
Trade trade = sharedModel.getTrade();
Contract contract = new Contract(trade.getOffer(),
trade.getTradeAmount(),
trade.getTakeOfferFeeTxId(),
sharedModel.getPeersAccountId(),
sharedModel.getUser().getAccountID(),
sharedModel.getPeersBankAccount(),
sharedModel.getUser().getCurrentBankAccount(),
trade.getOffer().getMessagePubKeyAsHex(),
sharedModel.getUser().getMessagePubKeyAsHex()
);
String contractAsJson = Utilities.objectToJson(contract);
String signature = sharedModel.getCryptoFacade().signContract(sharedModel.getWalletFacade().getRegistrationAddressInfo().getKey(), contractAsJson);
//log.trace("contract: " + contract);
//log.debug("contractAsJson: " + contractAsJson);
//log.trace("contract signature: " + signature);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setContractTakerSignature(signature);
complete();
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.payment.taker.listeners.GetPeerAddressListener;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetPeerAddress extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public GetPeerAddress(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
sharedModel.getMessageFacade().getPeerAddress(sharedModel.getTrade().getOffer().getMessagePubKeyAsHex(), new GetPeerAddressListener()
{
@Override
public void onResult(PeerAddress address)
{
log.trace("Received address = " + address.toString());
sharedModel.setPeerAddress(address);
complete();
}
@Override
public void onFailed()
{
log.error("Lookup for peer address failed.");
failed(new Exception("Lookup for peer address failed."));
}
});
}
}

View file

@ -0,0 +1,47 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import io.bitsquare.trade.Trade;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayDeposit extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(PayDeposit.class);
public PayDeposit(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
Trade trade = sharedModel.getTrade();
BigInteger collateralAmount = trade.getCollateralAmount();
sharedModel.setSignedTakerDepositTx(sharedModel.getWalletFacade().takerAddPaymentAndSignTx(trade.getTradeAmount().add(collateralAmount),
trade.getTradeAmount().add(collateralAmount).add(collateralAmount),
sharedModel.getOffererPubKey(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString(),
trade.getOffer().getArbitrator().getPubKeyAsHex(),
sharedModel.getPreparedOffererDepositTxAsHex(),
trade.getId()));
log.trace("sharedModel.signedTakerDepositTx: " + sharedModel.getSignedTakerDepositTx());
sharedModel.setTakerTxOutIndex(sharedModel.getSignedTakerDepositTx().getInput(1).getOutpoint().getIndex());
complete();
} catch (InsufficientMoneyException e)
{
log.error("Pay deposit failed due InsufficientMoneyException " + e);
failed(new Exception("Pay deposit failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,50 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayTakeOfferFee extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(PayTakeOfferFee.class);
public PayTakeOfferFee(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.getWalletFacade().payTakeOfferFee(sharedModel.getTrade().getId(), new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("Take offer fee paid successfully. Transaction ID = " + transaction.getHashAsString());
sharedModel.getTrade().setTakeOfferFeeTxID(transaction.getHashAsString());
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("Take offer fee paid failed with exception: " + t);
failed(new Exception("Take offer fee paid failed with exception: " + t));
}
});
} catch (InsufficientMoneyException e)
{
log.error("Take offer fee paid failed due InsufficientMoneyException " + e);
failed(new Exception("Take offer fee paid failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.RequestTakeOfferMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakeOffer extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(RequestTakeOffer.class);
public RequestTakeOffer(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestTakeOfferMessage msg = new RequestTakeOfferMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradingMessage(sharedModel.getPeerAddress(), msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakeOfferMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestTakeOfferMessage failed to arrive at peer");
failed(new Exception("RequestTakeOfferMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,42 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendPayoutTxToOfferer extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxToOfferer.class);
public SendPayoutTxToOfferer(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(sharedModel.getTrade().getId(), sharedModel.getPayoutTxAsHex());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("PayoutTxPublishedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("PayoutTxPublishedMessage failed to arrive at peer");
failed(new Exception("PayoutTxPublishedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,55 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.Utils;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedTakerDepositTxAsHex extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class);
public SendSignedTakerDepositTxAsHex(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(sharedModel.getTrade().getId(),
sharedModel.getUser().getCurrentBankAccount(),
sharedModel.getUser().getAccountID(),
sharedModel.getUser().getMessagePubKeyAsHex(),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().bitcoinSerialize()),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().getInput(1).getScriptBytes()),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize()),
sharedModel.getTrade().getContractAsJson(),
sharedModel.getTrade().getTakerSignature(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getAddressString(),
sharedModel.getTakerTxOutIndex(),
sharedModel.getOffererTxOutIndex()
);
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestOffererDepositPublicationMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestOffererDepositPublicationMessage failed to arrive at peer");
failed(new Exception("RequestOffererDepositPublicationMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,46 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendTakeOfferFeePayedTxId extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendTakeOfferFeePayedTxId.class);
public SendTakeOfferFeePayedTxId(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
TakeOfferFeePayedMessage msg = new TakeOfferFeePayedMessage(sharedModel.getTrade().getId(),
sharedModel.getTrade().getTakeOfferFeeTxId(),
sharedModel.getTrade().getTradeAmount(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getPubKeyAsHexString());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("TakeOfferFeePayedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("TakeOfferFeePayedMessage failed to arrive at peer");
failed(new Exception("TakeOfferFeePayedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,66 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishPayoutTx extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishPayoutTx.class);
public SignAndPublishPayoutTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
String depositTxAsHex = sharedModel.getDepositTxAsHex();
String offererSignatureR = sharedModel.getOffererSignatureR();
String offererSignatureS = sharedModel.getOffererSignatureS();
BigInteger offererPaybackAmount = sharedModel.getOffererPaybackAmount();
BigInteger takerPaybackAmount = sharedModel.getTakerPaybackAmount();
String offererPayoutAddress = sharedModel.getOffererPayoutAddress();
sharedModel.getWalletFacade().takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress,
sharedModel.getTrade().getId(),
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("takerSignsAndSendsTx " + transaction);
sharedModel.getListener().onTradeCompleted(sharedModel.getTrade(), transaction.getHashAsString());
sharedModel.setPayoutTxAsHex(Utils.bytesToHexString(transaction.bitcoinSerialize()));
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("Exception at takerSignsAndSendsTx " + t);
failed(t);
}
});
} catch (Exception e)
{
log.error("Exception at takerSignsAndSendsTx " + e);
failed(e);
}
}
}

View file

@ -0,0 +1,42 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyOffererAccount extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyOffererAccount.class);
public VerifyOffererAccount(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet
if (sharedModel.getBlockChainFacade().verifyAccountRegistration())
{
if (sharedModel.getBlockChainFacade().isAccountBlackListed(sharedModel.getPeersAccountId(), sharedModel.getPeersBankAccount()))
{
log.error("Offerer is blacklisted");
failed(new Exception("Offerer is blacklisted"));
}
else
{
complete();
}
}
else
{
log.error("Account Registration for peer failed.");
failed(new Exception("Account Registration for peer failed."));
}
}
}

View file

@ -36,7 +36,7 @@ public class DSAKeyUtil
public static PublicKey getPublicKeyFromHexString(String publicKeyAsHex) throws NoSuchAlgorithmException, InvalidKeySpecException public static PublicKey getPublicKeyFromHexString(String publicKeyAsHex) throws NoSuchAlgorithmException, InvalidKeySpecException
{ {
final byte[] bytes = Utils.parseAsHexOrBase58(publicKeyAsHex); final byte[] bytes = Utils.parseAsHexOrBase58(publicKeyAsHex);
final KeyFactory keyFactory = KeyFactory.INSTANCE("DSA"); final KeyFactory keyFactory = KeyFactory.GET_INSTANCE("DSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(bytes)); return keyFactory.generatePublic(new X509EncodedKeySpec(bytes));
} */ } */
@ -60,7 +60,7 @@ public class DSAKeyUtil
} catch (Throwable throwable) } catch (Throwable throwable)
{ {
if (throwable instanceof FileNotFoundException) if (throwable instanceof FileNotFoundException)
log.debug("Files not found. That is ok for the first run."); log.debug("Files not found. That is ok for the first execute.");
else else
log.error("Could not read key files. " + throwable); log.error("Could not read key files. " + throwable);

View file

@ -0,0 +1,37 @@
package io.nucleo.scheduler;
import com.sun.istack.internal.NotNull;
import io.nucleo.scheduler.worker.AbstractWorker;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractScheduler extends AbstractWorker implements WorkerResultHandler, WorkerFaultHandler
{
protected List<Worker> workerElements = new ArrayList<>();
public void setWorkers(@NotNull List<Worker> workerElements)
{
this.workerElements = workerElements;
}
protected void executeWorker(Worker worker)
{
((AbstractWorker) worker).setModel(model);
worker.addResultHandlers(this);
worker.addFaultHandlers(this);
worker.execute();
}
public void onResult()
{
}
public void onFault(Throwable throwable)
{
failed(throwable);
}
}

View file

@ -0,0 +1,47 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.tasks.AbstractDependencyManagedTask;
import io.nucleo.scheduler.worker.AbstractWorker;
import io.nucleo.scheduler.worker.Worker;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Not tested yet as not used...
*/
public class DependencyManagedScheduler extends AbstractScheduler
{
private static final Logger log = LoggerFactory.getLogger(DependencyManagedScheduler.class);
@Override
public void execute()
{
if (workerElements.size() > 0)
workerElements.stream().forEach(this::executeWorker);
else
complete();
}
@Override
protected void executeWorker(Worker worker)
{
((AbstractWorker) worker).setModel(model);
if (((AbstractDependencyManagedTask) worker).areAllDependenciesAvailable())
{
worker.addResultHandlers(this);
worker.addFaultHandlers(this);
worker.execute();
}
}
@Override
public void onResult(Worker worker)
{
Predicate<Worker> notCompleted = w -> !w.getHasCompleted();
if (workerElements.stream().filter(notCompleted).count() == 0)
complete();
else
execute();
}
}

View file

@ -0,0 +1,34 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.worker.Worker;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Not tested yet as not used...
*/
public class ParallelScheduler extends AbstractScheduler
{
private static final Logger log = LoggerFactory.getLogger(ParallelScheduler.class);
private long numberOfChildrenCompleted;
@Override
public void execute()
{
if (workerElements.size() > 0)
workerElements.stream().forEach(this::executeWorker);
else
complete();
}
@Override
public void onResult(Worker worker)
{
Predicate<Worker> notCompleted = w -> !w.getHasCompleted();
if (workerElements.stream().filter(notCompleted).count() == 0)
complete();
}
}

View file

@ -0,0 +1,44 @@
package io.nucleo.scheduler;
import com.sun.istack.internal.NotNull;
import io.nucleo.scheduler.worker.Worker;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class SequenceScheduler extends AbstractScheduler
{
private Iterator<Worker> workerIterator;
public SequenceScheduler(List<Worker> workerElements, Object model)
{
setWorkers(workerElements);
setModel(model);
}
public SequenceScheduler()
{
}
@Override
public void setWorkers(@NotNull List<Worker> workerElements)
{
workerIterator = new LinkedList<>(workerElements).iterator();
}
@Override
public void execute()
{
if (workerIterator != null && workerIterator.hasNext())
executeWorker(workerIterator.next());
else
complete();
}
@Override
public void onResult(Worker worker)
{
execute();
}
}

View file

@ -0,0 +1,15 @@
package io.nucleo.scheduler.example;
import io.nucleo.scheduler.model.PropertyProviderModel;
public class ExamplePropertyProviderModel extends PropertyProviderModel
{
public final Object flashVars;
public Object user;
public ExamplePropertyProviderModel(Object flashVars)
{
this.flashVars = flashVars;
}
}

View file

@ -0,0 +1,48 @@
package io.nucleo.scheduler.example;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SchedulerTestRunner implements WorkerResultHandler, WorkerFaultHandler
{
private static final Logger log = LoggerFactory.getLogger(SchedulerTestRunner.class);
private static SchedulerTestRunner schedulerTestRunner;
public SchedulerTestRunner()
{
/* Map<Object, Object> flashVars = new HashMap<>();
flashVars.put("userName", "bully");
Object model = new ExamplePropertyProviderModel(flashVars);
ExampleAS3Scheduler exampleScheduler = new ExampleAS3Scheduler();
exampleScheduler.setModel(model);
exampleScheduler.setResultHandler(() -> {
log.debug("setResultHandler ");
});
exampleScheduler.setFaultHandler((throwable) -> {
log.debug("setFaultHandler ");
});
exampleScheduler.execute(); */
}
public static void main(String[] args)
{
schedulerTestRunner = new SchedulerTestRunner();
}
@Override
public void onFault(Throwable throwable)
{
log.debug("onFault " + this);
}
@Override
public void onResult(Worker worker)
{
log.debug("onResult " + this);
}
}

View file

@ -0,0 +1,54 @@
package io.nucleo.scheduler.model;
import java.lang.reflect.Field;
import java.util.List;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class PropertyProviderModel
{
private static final Logger log = LoggerFactory.getLogger(PropertyProviderModel.class);
public PropertyProviderModel()
{
}
public boolean areAllDependenciesAvailable(List<String> propertyNames)
{
Predicate<String> isPropertyNotNull = (propertyName) -> read(propertyName) != null;
return propertyNames.stream().allMatch(isPropertyNotNull);
}
public Object read(String key)
{
try
{
return getField(key).get(this);
} catch (IllegalAccessException e)
{
e.printStackTrace();
return null;
}
}
// -------------------------------------------------------------------
// Private
// -------------------------------------------------------------------
private Field getField(String key)
{
Class clazz = this.getClass();
try
{
Field field = clazz.getDeclaredField(key);
field.setAccessible(true); // make sure a private field is accessible for reflection
return field;
} catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,68 @@
package io.nucleo.scheduler.tasks;
import io.nucleo.scheduler.model.PropertyProviderModel;
import java.util.ArrayList;
import java.util.List;
/**
* The base class for all tasks using a IPropertyProviderModel instance as a shared model.
*/
public abstract class AbstractDependencyManagedTask extends AbstractTask
{
private PropertyProviderModel propertyProviderModel;
private List<String> readDependencyKeys = new ArrayList<>();
public AbstractDependencyManagedTask()
{
}
// -------------------------------------------------------------------
// IRunnable implementation
// -------------------------------------------------------------------
@Override
public void setModel(Object model)
{
propertyProviderModel = (PropertyProviderModel) model;
super.setModel(model);
initReadDependencies();
}
// -------------------------------------------------------------------
// Abstract Methods
// -------------------------------------------------------------------
/**
* To be overwritten in subclasses
* Used to read the needed data objects for the task.
* Typically stored as instance variable in the task.
*/
protected void initReadDependencies()
{
// user = read(IUser);
// channel = read("channel");
}
// -------------------------------------------------------------------
// Final protected
// -------------------------------------------------------------------
final protected Object read(String key)
{
readDependencyKeys.add(key);
return propertyProviderModel.read(key);
}
public boolean areAllDependenciesAvailable()
{
return propertyProviderModel.areAllDependenciesAvailable(readDependencyKeys);
}
}

View file

@ -0,0 +1,13 @@
package io.nucleo.scheduler.tasks;
import io.nucleo.scheduler.worker.AbstractWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The base class for all tasks.
*/
public abstract class AbstractTask extends AbstractWorker
{
private static final Logger log = LoggerFactory.getLogger(AbstractTask.class);
}

View file

@ -0,0 +1,55 @@
package io.nucleo.scheduler.worker;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractWorker implements Worker
{
protected List<WorkerResultHandler> resultHandlers = new ArrayList<>();
protected List<WorkerFaultHandler> faultHandlers = new ArrayList<>();
protected Object model;
protected boolean hasFailed;
protected boolean hasCompleted;
@Override
abstract public void execute();
@Override
public void addResultHandlers(WorkerResultHandler resultHandler)
{
resultHandlers.add(resultHandler);
}
@Override
public void addFaultHandlers(WorkerFaultHandler faultHandler)
{
faultHandlers.add(faultHandler);
}
public void setModel(Object model)
{
this.model = model;
}
@Override
public boolean getHasCompleted()
{
return hasCompleted;
}
protected void complete()
{
hasCompleted = true;
resultHandlers.stream().forEach(e -> e.onResult(this));
}
protected void failed(Throwable throwable)
{
hasFailed = true;
faultHandlers.stream().forEach(e -> e.onFault(throwable));
}
protected void destroy()
{
}
}

View file

@ -0,0 +1,19 @@
package io.nucleo.scheduler.worker;
/**
* The base interface for all runnable objects (tasks, schedulers)
*/
public interface Worker
{
/**
* Starts the execution.
*/
void execute();
void addResultHandlers(WorkerResultHandler resultHandler);
void addFaultHandlers(WorkerFaultHandler faultHandler);
boolean getHasCompleted();
}

View file

@ -0,0 +1,6 @@
package io.nucleo.scheduler.worker;
public interface WorkerFaultHandler
{
void onFault(Throwable throwable);
}

View file

@ -0,0 +1,6 @@
package io.nucleo.scheduler.worker;
public interface WorkerResultHandler
{
void onResult(Worker worker);
}

View file

@ -1,9 +1,9 @@
package io.bitsquare; package io.bitsquare;
import io.bitsquare.btc.BtcValidatorTest; import io.bitsquare.btc.BtcValidatorTest;
import io.bitsquare.currency.BitcoinTest;
import io.bitsquare.gui.util.BitSquareConverterTest; import io.bitsquare.gui.util.BitSquareConverterTest;
import io.bitsquare.gui.util.BitSquareValidatorTest; import io.bitsquare.gui.util.BitSquareValidatorTest;
import io.nucleo.scheduler.SequenceSchedulerTest;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
@ -12,7 +12,7 @@ import org.junit.runners.Suite;
BtcValidatorTest.class, BtcValidatorTest.class,
BitSquareConverterTest.class, BitSquareConverterTest.class,
BitSquareValidatorTest.class, BitSquareValidatorTest.class,
BitcoinTest.class SequenceSchedulerTest.class
}) })
public class BitSquareTestSuite public class BitSquareTestSuite

View file

@ -19,16 +19,16 @@ public class BtcValidatorTest
amount = BigInteger.ZERO; amount = BigInteger.ZERO;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount)); assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr; amount = FeePolicy.TX_FEE;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount)); assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = Transaction.MIN_NONDUST_OUTPUT; amount = Transaction.MIN_NONDUST_OUTPUT;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount)); assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT); amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT);
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount)); assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT).add(BigInteger.ONE); amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(BigInteger.ONE);
assertTrue("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount)); assertTrue("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
} }
} }

View file

@ -1,50 +0,0 @@
package io.bitsquare.currency;
import com.google.bitcoin.core.Transaction;
import java.math.BigInteger;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BitcoinTest
{
@Test
public void testBitcoin()
{
Bitcoin bitcoin;
Bitcoin compareValue;
bitcoin = new Bitcoin("0");
assertTrue(bitcoin.isZero());
bitcoin = new Bitcoin("0");
compareValue = new Bitcoin("1");
assertTrue(bitcoin.isLess(compareValue));
assertFalse(compareValue.isLess(bitcoin));
assertFalse(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin("1");
compareValue = new Bitcoin("0");
assertFalse(bitcoin.isLess(compareValue));
assertTrue(compareValue.isLess(bitcoin));
assertFalse(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin("1");
compareValue = new Bitcoin("1");
assertFalse(bitcoin.isLess(compareValue));
assertFalse(compareValue.isLess(bitcoin));
assertTrue(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT);
assertTrue(bitcoin.isMinValue());
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT.subtract(BigInteger.ONE));
assertFalse(bitcoin.isMinValue());
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT.add(BigInteger.ONE));
assertTrue(bitcoin.isMinValue());
}
}

View file

@ -1,5 +1,14 @@
package io.bitsquare.gui.util; package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.locale.Country;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.user.Arbitrator;
import java.math.BigInteger;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@ -34,5 +43,37 @@ public class BitSquareValidatorTest
assertFalse(BitSquareValidator.validateStringNotEmpty(null)); assertFalse(BitSquareValidator.validateStringNotEmpty(null));
} }
@Test
public void testTradeAmountOutOfRange()
{
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.ZERO, new MockOffer(BigInteger.TEN, BigInteger.valueOf(0))));
assertTrue(BitSquareValidator.tradeAmountOutOfRange(BigInteger.ZERO, new MockOffer(BigInteger.TEN, BigInteger.valueOf(1))));
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.TEN, BigInteger.valueOf(0))));
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.TEN, BigInteger.valueOf(2))));
assertTrue(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.ONE, BigInteger.valueOf(0))));
}
@Test
public void testGreaterThanZero()
{
assertFalse(BitSquareValidator.greaterThanZero(BigInteger.ZERO));
assertFalse(BitSquareValidator.greaterThanZero(BigInteger.valueOf(-1)));
assertTrue(BitSquareValidator.greaterThanZero(BigInteger.ONE));
}
public static class MockOffer extends Offer
{
public MockOffer(BigInteger amount, BigInteger minAmount)
{
super(null, null, 0, amount, minAmount, null, null, null, null, null, 0, null, null);
}
public MockOffer(String messagePubKeyAsHex, Direction direction, double price, BigInteger amount, BigInteger minAmount, BankAccountType bankAccountType, Currency currency, Country bankAccountCountry, String bankAccountUID, Arbitrator arbitrator, int collateral, List<Country> acceptedCountries, List<Locale> acceptedLanguageLocales)
{
super(messagePubKeyAsHex, direction, price, amount, minAmount, bankAccountType, currency, bankAccountCountry, bankAccountUID, arbitrator, collateral, acceptedCountries, acceptedLanguageLocales);
}
}
} }

View file

@ -0,0 +1,228 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.tasks.SyncWorker1;
import io.nucleo.scheduler.tasks.SyncWorker2;
import io.nucleo.scheduler.worker.Worker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class SequenceSchedulerTest
{
private final boolean[] hasCompleted = new boolean[1];
private final boolean[] hasFailed = new boolean[1];
private final boolean[] worker1HasCompleted = new boolean[1];
private final boolean[] worker1HasFailed = new boolean[1];
private final boolean[] worker2HasCompleted = new boolean[1];
private final boolean[] worker2HasFailed = new boolean[1];
private SequenceScheduler sequenceScheduler;
private Map<String, String> model = new HashMap<>();
private List<Worker> workerList = new ArrayList<>();
private Throwable worker1Throwable;
@Before
public void setUp()
{
sequenceScheduler = new SequenceScheduler();
sequenceScheduler.addResultHandlers((worker) -> {
hasCompleted[0] = true;
});
sequenceScheduler.addFaultHandlers(throwable -> {
hasFailed[0] = true;
});
}
@After
public void tearDown() throws Exception
{
hasCompleted[0] = false;
hasFailed[0] = false;
workerList.clear();
model.clear();
worker1Throwable = null;
}
@Test
public void testEmpty()
{
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
}
@Test
public void testEmpty2()
{
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
}
@Test
public void testOneWithCompleted()
{
Worker worker1 = getWorker1(false);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
}
@Test
public void testOneWithFailed()
{
Worker worker1 = getWorker1(true);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertFalse(worker1.getHasCompleted());
assertFalse(worker1HasCompleted[0]);
assertTrue(worker1HasFailed[0]);
assertEquals(SyncWorker1.ERR_MSG, worker1Throwable.getMessage());
}
// @Test
public void testTwoCompleted()
{
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(false);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertTrue(worker2.getHasCompleted());
assertTrue(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]);
}
@Test
public void testTwoReverseOrder()
{
model.put("worker1State", "");
model.put("worker2State", "");
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(false);
workerList.add(worker2);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.setModel(model);
sequenceScheduler.execute();
assertEquals(SyncWorker1.STATE, model.get("worker1State"));
assertEquals(SyncWorker2.STATE, model.get("worker2State"));
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertTrue(worker2.getHasCompleted());
assertTrue(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]);
}
@Test
public void testTwoFirstFailed()
{
Worker worker1 = getWorker1(true);
Worker worker2 = getWorker2(false);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertFalse(worker1.getHasCompleted());
assertFalse(worker1HasCompleted[0]);
assertTrue(worker1HasFailed[0]);
assertFalse(worker2.getHasCompleted());
assertFalse(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]); // second has not been executed and is not failed!
}
@Test
public void testTwoSecondFailed()
{
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(true);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertFalse(worker2.getHasCompleted());
assertFalse(worker2HasCompleted[0]);
assertTrue(worker2HasFailed[0]); // second has not been executed and is not failed!
}
private Worker getWorker1(boolean letItFail)
{
Worker worker1 = new SyncWorker1(letItFail);
worker1.addResultHandlers((worker) -> {
worker1HasCompleted[0] = true;
});
worker1.addFaultHandlers(throwable -> {
worker1HasFailed[0] = true;
worker1Throwable = throwable;
});
return worker1;
}
private Worker getWorker2(boolean letItFail)
{
Worker worker2 = new SyncWorker2(letItFail);
worker2.addResultHandlers((worker) -> {
worker2HasCompleted[0] = true;
});
worker2.addFaultHandlers(throwable -> {
worker2HasFailed[0] = true;
});
return worker2;
}
}

View file

@ -0,0 +1,29 @@
package io.nucleo.scheduler.tasks;
import java.util.Map;
public class SyncWorker1 extends AbstractTask
{
public static String ERR_MSG = "Failure message";
public static String STATE = "ok";
private boolean letItFail;
public SyncWorker1(boolean letItFail)
{
this.letItFail = letItFail;
}
@Override
public void execute()
{
System.out.println("execute " + this);
if (model != null) ((Map<String, String>) model).put("worker1State", STATE);
if (letItFail)
failed(new Exception(ERR_MSG));
else
complete();
}
}

View file

@ -0,0 +1,27 @@
package io.nucleo.scheduler.tasks;
import java.util.Map;
public class SyncWorker2 extends AbstractTask
{
public static String ERR_MSG = "Failure message";
public static String STATE = "ok";
private boolean letItFail;
public SyncWorker2(boolean letItFail)
{
this.letItFail = letItFail;
}
@Override
public void execute()
{
System.out.println("execute " + this);
if (model != null) ((Map<String, String>) model).put("worker2State", STATE);
if (letItFail)
failed(new Exception(ERR_MSG));
else
complete();
}
}